¿Cómo contar las apariciones de un elemento en una matriz Swift?

He visto algunos ejemplos de esto, pero todos parecen confiar en saber a qué elemento desea contar las ocurrencias. Mi matriz se genera dinámicamente, así que no tengo forma de saber qué elemento quiero contar las apariciones de (quiero contar las apariciones de todos ellos). ¿Alguien puede aconsejar?

Gracias por adelantado

EDITAR:

Tal vez debería haber sido más claro, la matriz contendrá múltiples cadenas diferentes (por ejemplo, ["FOO", "FOO", "BAR", "FOOBAR"]

¿Cómo puedo contar las ocurrencias de foo, bar y foobar sin saber qué son de antemano?

Swift 3 y Swift 2:

Puede usar un dictionary de tipo [String: Int] para acumular counts para cada uno de los elementos en su [String] :

 let arr = ["FOO", "FOO", "BAR", "FOOBAR"] var counts: [String: Int] = [:] for item in arr { counts[item] = (counts[item] ?? 0) + 1 } print(counts) // "[BAR: 1, FOOBAR: 1, FOO: 2]" for (key, value) in counts { print("\(key) occurs \(value) time(s)") } 

salida:

 BAR occurs 1 time(s) FOOBAR occurs 1 time(s) FOO occurs 2 time(s) 

Swift 4:

Swift 4 introduce (SE-0165) la capacidad de include un valor pnetworkingeterminado con una búsqueda de dictionary, y el valor resultante puede ser mutado con operaciones como += y -= , por lo que:

 counts[item] = (counts[item] ?? 0) + 1 

se convierte en:

 counts[item, default: 0] += 1 

Eso hace que sea fácil hacer la operación de conteo en una línea concisa usando forEach :

 let arr = ["FOO", "FOO", "BAR", "FOOBAR"] var counts: [String: Int] = [:] arr.forEach { counts[$0, default: 0] += 1 } print(counts) // "["FOOBAR": 1, "FOO": 2, "BAR": 1]" 
 array.filter{$0 == element}.count 

Actualicé la respuesta de oisdk a Swift2.

16/04/14 Actualicé este código a Swift2.2

10/16/11 actualizado a Swift3


Hashable:

 extension Sequence where Self.Iterator.Element: Hashable { private typealias Element = Self.Iterator.Element func freq() -> [Element: Int] { return networkinguce([:]) { (accu: [Element: Int], element) in var accu = accu accu[element] = accu[element]?.advanced(by: 1) ?? 1 return accu } } } 

Equivocado:

 extension Sequence where Self.Iterator.Element: Equatable { private typealias Element = Self.Iterator.Element func freqTuple() -> [(element: Element, count: Int)] { let empty: [(Element, Int)] = [] return networkinguce(empty) { (accu: [(Element, Int)], element) in var accu = accu for (index, value) in accu.enumerated() { if value.0 == element { accu[index].1 += 1 return accu } } return accu + [(element, 1)] } } } 

Uso

 let arr = ["a", "a", "a", "a", "b", "b", "c"] print(arr.freq()) // ["b": 2, "a": 4, "c": 1] print(arr.freqTuple()) // [("a", 4), ("b", 2), ("c", 1)] 

 for (k, v) in arr.freq() { print("\(k) -> \(v) time(s)") } // b -> 2 time(s) // a -> 4 time(s) // c -> 1 time(s) for (element, count) in arr.freqTuple() { print("\(element) -> \(count) time(s)") } // a -> 4 time(s) // b -> 2 time(s) // c -> 1 time(s) 

Con Swift 4, de acuerdo con sus necesidades, puede elegir uno de los 6 códigos de Zona de juegos siguientes para contar las apariciones de objects hashable en una matriz.


# 1. Al usar Array networkinguce(into:_:) y el subscript(_:default:) Dictionary subscript(_:default:) (requiere Swift 4)

 let array = [4, 23, 97, 97, 97, 23] let networkingucedArray = array.networkinguce(into: [:]) { counts, number in counts[number, default: 0] += 1 } print(networkingucedArray) // [4: 1, 23: 2, 97: 3] 

# 2. Utilizando la function repeatElement(_:count:) , la function zip(_:_:) , el init(_:uniquingKeysWith:) Dictionary init(_:uniquingKeysWith:) inicializador y la devolución de un Dictionary (requiere Swift 4)

 let array = [4, 23, 97, 97, 97, 23] let repeated = repeatElement(1, count: array.count) //let repeated = Array(repeating: 1, count: array.count) // also works let zipSequence = zip(array, repeated) let dictionary = Dictionary(zipSequence, uniquingKeysWith: { (current, new) in return current + new }) //let dictionary = Dictionary(zipSequence, uniquingKeysWith: +) // also works print(dictionary) // prints [4: 1, 23: 2, 97: 3] 

# 3. Usando un Dictionary init(grouping:by:) inicializador y devolviendo una Array de tuplas (requiere Swift 4)

 let array = [4, 23, 97, 97, 97, 23] let dictionary = Dictionary(grouping: array, by: { $0 }) let newArray = dictionary.map { (key: Int, value: [Int]) in return (key, value.count) } print(newArray) // prints: [(4, 1), (23, 2), (97, 3)] 

# 4. Usando un bucle for y devolviendo un Dictionary

 extension Array where Element: Hashable { func countForElements() -> [Element: Int] { var counts = [Element: Int]() for element in self { counts[element] = (counts[element] ?? 0) + 1 } return counts } } let array = [4, 23, 97, 97, 97, 23] print(array.countForElements()) // prints [4: 1, 23: 2, 97: 3] 

# 5. Usando NSCountedSet , método de map y devolviendo una Array de tuplas (requiere Foundation)

 import Foundation extension Array where Element: Hashable { func countForElements() -> [(Element, Int)] { let countedSet = NSCountedSet(array: self) let res = countedSet.objectEnumerator().map { (object: Any) -> (Element, Int) in return (object as! Element, countedSet.count(for: object)) } return res } } let array = [4, 23, 97, 97, 97, 23] print(array.countForElements()) // prints [(97, 3), (4, 1), (23, 2)] 

# 6. Usando NSCountedSet , AnyIterator y devolviendo una Array de tuplas (requiere Foundation)

 import Foundation extension Array where Element: Hashable { func counForElements() -> Array<(Element, Int)> { let countedSet = NSCountedSet(array: self) var countedSetIterator = countedSet.objectEnumerator().makeIterator() let anyIterator = AnyIterator<(Element, Int)> { guard let element = countedSetIterator.next() as? Element else { return nil } return (element, countedSet.count(for: element)) } return Array<(Element, Int)>(anyIterator) } } let array = [4, 23, 97, 97, 97, 23] print(array.counForElements()) // [(97, 3), (4, 1), (23, 2)] 

Créditos:

  • Swift Modismos
  • genérico en la colección, usando dictionary

Use un NSCountedSet. En Objective-C:

 NSCountedSet* countedSet = [[NSCountedSet alloc] initWithArray:array]; for (NSString* string in countedSet) NSLog (@"String %@ occurs %zd times", string, [countedSet countForObject:string]); 

Supongo que puedes traducir esto a Swift tú mismo.

Qué tal si:

 func freq<S: SequenceType where S.Generator.Element: Hashable>(seq: S) -> [S.Generator.Element:Int] { return networkinguce(seq, [:]) { (var accu: [S.Generator.Element:Int], element) in accu[element] = accu[element]?.successor() ?? 1 return accu } } freq(["FOO", "FOO", "BAR", "FOOBAR"]) // ["BAR": 1, "FOOBAR": 1, "FOO": 2] 

Es genérico, por lo que funcionará con lo que sea su elemento, siempre y cuando sea hashable:

 freq([1, 1, 1, 2, 3, 3]) // [2: 1, 3: 2, 1: 3] freq([true, true, true, false, true]) // [false: 1, true: 4] 

Y, si no puedes hacer que tus elementos sean hashable, puedes hacerlo con tuplas:

 func freq<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [(S.Generator.Element, Int)] { let empty: [(S.Generator.Element, Int)] = [] return networkinguce(seq, empty) { (var accu: [(S.Generator.Element,Int)], element) in for (index, value) in enumerate(accu) { if value.0 == element { accu[index].1++ return accu } } return accu + [(element, 1)] } } freq(["a", "a", "a", "b", "b"]) // [("a", 3), ("b", 2)] 

Otro enfoque sería utilizar el método de filter. Me parece que el más elegante

 var numberOfOccurenses = countedItems.filter( { if $0 == "FOO" || $0 == "BAR" || $0 == "FOOBAR" { return true }else{ return false } }).count 

Me gusta evitar loops internos y usar .map tanto como sea posible. Entonces, si tenemos una matriz de cadenas, podemos hacer lo siguiente para contar las ocurrencias.

 var occurances = ["tuples", "are", "awesome", "tuples", "are", "cool", "tuples", "tuples", "tuples", "shades"] var dict:[String:Int] = [:] occurances.map{ if let val: Int = dict[$0] { dict[$0] = val+1 } else { dict[$0] = 1 } } 

huellas dactilares

 ["tuples": 5, "awesome": 1, "are": 2, "cool": 1, "shades": 1] 

Swift 4

 let array = ["FOO", "FOO", "BAR", "FOOBAR"] // Merging keys with closure for conflicts let mergedKeysAndValues = Dictionary(zip(array, repeatElement(1, count: array)), uniquingKeysWith: +) // mergedKeysAndValues is ["FOO": 2, "BAR": 1, "FOOBAR": 1] 

Primer paso en el recuento de sorting.

 var inputList = [9,8,5,6,4,2,2,1,1] var countList : [Int] = [] var max = inputList.maxElement()! // Iniate an array with specific Size and with intial value. // We made the Size to max+1 to integrate the Zero. We intiated the array with Zeros because it's Counting. var countArray = [Int](count: Int(max + 1), repeatedValue: 0) for num in inputList{ countArray[num] += 1 } print(countArray)