Most common array elements

Refresh

December 2018

Views

2.2k time

4

I need to find the most common (modal) elements in an array.

The simplest way I could think of was to set variables for each unique element, and assign a count variable for each one, which increases every time it is recorded in a for loop which runs through the array.

Unfortunately the size of the array is unknown and will be very large, so this method is useless.

I have come across a similar question in Objective-C that uses an NSCountedSet method to rank the array elements. Unfortunately I am very new to programming, and could only translate the first line into Swift.

The suggested method is as follows:

    var yourArray: NSArray! // My swift translation

    NSCountedSet *set = [[NSCountedSet alloc] initWithArray:yourArray];

    NSMutableDictionary *dict=[NSMutableDictionary new];

    for (id obj in set) {
        [dict setObject:[NSNumber numberWithInteger:[set countForObject:obj]]
            forKey:obj]; //key is date
    }

    NSLog(@"Dict : %@", dict);

    NSMutableArray *top3=[[NSMutableArray alloc]initWithCapacity:3];

    //which dict obj is = max
    if (dict.count>=3) {

        while (top3.count<3) {
            NSInteger max = [[[dict allValues] valueForKeyPath:@"@max.intValue"] intValue];

            for (id obj in set) {
                if (max == [dict[obj] integerValue]) {
                    NSLog(@"--> %@",obj);
                    [top3 addObject:obj];
                    [dict removeObjectForKey:obj];
                }
            }
        }
    }

    NSLog(@"top 3 = %@", top3);

In my program I will need to find the top five place names in an array.

3 answers

0

То же скорости полета скорости, используя reduceвместо того , чтобы for-in:

extension Sequence where Self.Iterator.Element: Hashable {
    func frequencies() -> [(Self.Iterator.Element, Int)] {
        return reduce([:]) {
            var frequencies = $0
            frequencies[$1] = (frequencies[$1] ?? 0) + 1
            return frequencies
        }.sorted { $0.1 > $1.1 }
    }
}

Но обратите внимание , что здесь, используя reduceс structне столь эффективным , какfor-in из - за стоимости структуры копирования. Таким образом , вы, как правило , предпочитают for-inспособ сделать это.

[Править: Гоша, статья одним и тем же парнем, как топ-ответ]

14

Редактирование: теперь Swift 2,0 ниже

Не самый эффективный из решений, но простой:

let a = [1,1,2,3,1,7,4,6,7,2]

var frequency: [Int:Int] = [:]

for x in a {
    // set frequency to the current count of this element + 1
    frequency[x] = (frequency[x] ?? 0) + 1
}

let descending = sorted(frequency) { $0.1 > $1.1 }

descendingтеперь состоит из массива пара: значение и частота, отсортировано наиболее часто в первую очередь. Таким образом, «топ-5» будет первые 5 записей (предполагая, что было 5 или более различных значений). Не имеет значения, насколько большой массив источник.

Вот общая функция версия, которая будет работать на любой последовательности:

func frequencies
  <S: SequenceType where S.Generator.Element: Hashable>
  (source: S) -> [(S.Generator.Element,Int)] {

    var frequency: [S.Generator.Element:Int] = [:]

    for x in source {
        frequency[x] = (frequency[x] ?? 0) + 1
    }

    return sorted(frequency) { $0.1 > $1.1 }
}

frequencies(a)

Для Swift 2.0, вы можете адаптировать функцию быть расширением протокола:

extension SequenceType where Generator.Element: Hashable {
    func frequencies() -> [(Generator.Element,Int)] {

        var frequency: [Generator.Element:Int] = [:]

        for x in self {
            frequency[x] = (frequency[x] ?? 0) + 1
        }

        return frequency.sort { $0.1 > $1.1 }
    }
}

a.frequencies()

Для Swift 3.0:

extension Sequence where Self.Iterator.Element: Hashable {
    func frequencies() -> [(Self.Iterator.Element,Int)] {

        var frequency: [Self.Iterator.Element:Int] = [:]

        for x in self {
            frequency[x] = (frequency[x] ?? 0) + 1
        }

        return frequency.sorted { $0.1 > $1.1 }
    }
}
2

Для XCode 7.1 решение.

// Array of elements
let a = [7,3,2,1,4,6,8,9,5,3,0,7,2,7]

// Create a key for elements and their frequency
var times: [Int: Int] = [:]

// Iterate over the dictionary
for b in a {
    // Every time there is a repeat value add one to that key
    times[b] = (times[b] ?? 0) + 1
}

// This is for sorting the values
let decending = times.sort({$0.1 > $1.1})
// For sorting the keys the code would be 
// let decending = times.sort({$0.0 > $1.0})
// Do whatever you want with sorted array
print(decending)