arreglos array ios arrays swift nsarray

ios - arreglos - array xcode swift



¿Cómo contar las ocurrencias de un elemento en una matriz Swift? (13)

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

Gracias por adelantado

EDITAR:

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

¿Cómo puedo contar las ocurrencias de foo, bar y foobar sin saber de antemano cuáles son?


Con Swift 5, de acuerdo con sus necesidades, puede elegir uno de los 7 siguientes códigos de muestra de Playground para contar las ocurrencias de elementos que se pueden compartir en una matriz.

# 1 Utilizando la reduce(into:_:) Array reduce(into:_:) y el subscript(_:default:) Dictionary subscript(_:default:)

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

# 2 Uso de la función repeatElement(_:count:) , la función zip(_:_:) y la init(_:uniquingKeysWith:)

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 Uso de un Dictionary init(grouping:by:) inicializador y mapValues(_:) método

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

# 4. Uso de un Dictionary init(grouping:by:) inicializador y map(_:) método

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)]

# 5. Usar un bucle for y el subscript(_:) Dictionary subscript(_:)

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]

# 6. Uso del NSCountedSet y NSEnumerator ''s map(_:) (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)]

# 7. Uso de NSCountedSet y AnyIterator (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


Me gusta evitar los bucles internos y usar .map tanto como sea posible. Entonces, si tenemos una serie 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]


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

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


Primer paso en la cuenta de clasificación.

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)


Puede usar esta función para contar la ocurrencia de los elementos en la matriz

func checkItemCount(arr: [String]) { var dict = [String: Any]() for x in arr { var count = 0 for y in arr { if y == x { count += 1 } } dict[x] = count } print(dict) }

Puedes implementarlo así:

let arr = ["FOO", "FOO", "BAR", "FOOBAR"] checkItemCount(arr: arr)


Qué tal si:

func freq<S: SequenceType where S.Generator.Element: Hashable>(seq: S) -> [S.Generator.Element:Int] { return reduce(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 cualquier elemento que sea, siempre que sea hashaable:

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 se puedan compartir, 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 reduce(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)]


Use un NSCountedSet. En el objetivo-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 en Swift tú mismo.


Actualicé la respuesta de oisdk a Swift2.

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

16/10/11 actualizado a Swift3

Hashable:

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

Ecualizable:

extension Sequence where Self.Iterator.Element: Equatable { private typealias Element = Self.Iterator.Element func freqTuple() -> [(element: Element, count: Int)] { let empty: [(Element, Int)] = [] return reduce(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)


Swift 3 y Swift 2:

Puede usar un diccionario de tipo [String: Int] para crear recuentos 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 incluir un valor predeterminado con una búsqueda en el diccionario, y el valor resultante puede mutarse con operaciones como += y -= , por lo que:

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

se convierte en:

counts[item, default: 0] += 1

Eso facilita 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]"

Swift 4: reduce(into:_:)

Swift 4 presenta una nueva versión de reduce que utiliza una variable inout para acumular los resultados. Usando eso, la creación de los conteos realmente se convierte en una sola línea:

let arr = ["FOO", "FOO", "BAR", "FOOBAR"] let counts = arr.reduce(into: [:]) { counts, word in counts[word, default: 0] += 1 } print(counts) // ["BAR": 1, "FOOBAR": 1, "FOO": 2]

O usando los parámetros predeterminados:

let counts = arr.reduce(into: [:]) { $0[$1, default: 0] += 1 }

Finalmente, puede hacer de esto una extensión de Array para que se pueda Hashable en cualquier matriz que Hashable elementos Hashable :

extension Array where Element: Hashable { var histogram: [Element: Int] { return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 } } }

Esta idea fue tomada de esta pregunta, aunque la cambié a una propiedad calculada .


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]


extension Collection where Iterator.Element: Comparable & Hashable { func occurrencesOfElements() -> [Element: Int] { var counts: [Element: Int] = [:] let sortedArr = self.sorted(by: { $0 > $1 }) let uniqueArr = Set(sortedArr) if uniqueArr.count < sortedArr.count { sortedArr.forEach { counts[$0, default: 0] += 1 } } return counts } } // Testing with... [6, 7, 4, 5, 6, 0, 6].occurrencesOfElements() // Expected result (see number 6 occurs three times) : // [7: 1, 4: 1, 5: 1, 6: 3, 0: 1]


public extension Sequence { public func countBy<U : Hashable>(_ keyFunc: (Iterator.Element) -> U) -> [U: Int] { var dict: [U: Int] = [:] for el in self { let key = keyFunc(el) if dict[key] == nil { dict[key] = 1 } else { dict[key] = dict[key]! + 1 } //if case nil = dict[key]?.append(el) { dict[key] = [el] } } return dict } let count = ["a","b","c","a"].countBy{ $0 } // ["b": 1, "a": 2, "c": 1] struct Objc { var id: String = "" } let count = [Objc(id: "1"), Objc(id: "1"), Objc(id: "2"),Objc(id: "3")].countBy{ $0.id } // ["2": 1, "1": 2, "3": 1]


array.filter{$0 == element}.count