Comment compter les occurrences d'un élément dans une Swift tableau?
j'ai vu quelques exemples de cela, mais tous semblent compter sur le fait de savoir quel élément vous voulez compter les occurrences de. Mon tableau est généré dynamiquement, donc je n'ai aucun moyen de savoir quel élément je veux compter les occurrences de (je veux compter les occurrences de chacun d'eux). Quelqu'un peut-il conseiller?
Merci d'avance
EDIT:
peut-être que j'aurais dû être plus clair, le tableau contient plusieurs différentes chaînes (par exemple
["FOO", "FOO", "BAR", "FOOBAR"]
Comment puis-je compter les occurrences de foo, bar et foobar sans savoir ce qu'ils sont à l'avance?
11 réponses
Swift 3 et Swift 2:
Vous pouvez utiliser un dictionnaire de type [String: Int]
construire des comptes pour chacun des éléments dans votre [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)")
}
sortie:
BAR occurs 1 time(s)
FOOBAR occurs 1 time(s)
FOO occurs 2 time(s)
Swift 4:
Swift 4 introduit (SE-0165) la possibilité d'inclure une valeur par défaut avec un recherche dictionnaire, et la valeur qui en résulte peut être muté avec des opérations telles que +=
et -=
, donc:
counts[item] = (counts[item] ?? 0) + 1
devient:
counts[item, default: 0] += 1
qui rend facile de faire le comptage opération dans une ligne concise en utilisant forEach
:
let arr = ["FOO", "FOO", "BAR", "FOOBAR"]
var counts: [String: Int] = [:]
arr.forEach { counts["151940920", default: 0] += 1 }
print(counts) // "["FOOBAR": 1, "FOO": 2, "BAR": 1]"
Swift 4: reduce(into:_:)
Swift 4 introduit une nouvelle version de reduce
qui utilise une variable inout
pour accumuler les résultats. En utilisant cela, la création des comptes devient vraiment une seule ligne:
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]
ou en utilisant les paramètres par défaut:
let counts = arr.reduce(into: [:]) { "151960920"[, default: 0] += 1 }
enfin vous pouvez en faire une extension de Array
pour qu'il puisse être appelé sur n'importe quel tableau contenant Hashable
items:
extension Array where Element: Hashable {
var histogram: [Element: Int] {
return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 }
}
}
cette idée a été empruntée à cette question bien que je l'ai changé en un bien calculé .
avec Swift 4, en fonction de vos besoins, vous pouvez choisir l'un des 6 Codes de terrain de jeu suivants pour compter les occurrences d'articles hachables dans un tableau.
#1. En utilisant Array
reduce(into:_:)
et Dictionary
subscript(_:default:)
(nécessite Swift 4)
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. En utilisant la fonction repeatElement(_:count:)
, la fonction zip(_:_:)
, la fonction Dictionary
init(_:uniquingKeysWith:)
initialiseur et retour a Dictionary
(nécessite 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. Utilisation d'un Dictionary
init(grouping:by:)
initialiseur et retour d'un Array
de tuples (nécessite Swift 4)
let array = [4, 23, 97, 97, 97, 23]
let dictionary = Dictionary(grouping: array, by: { "151920920" })
let newArray = dictionary.map { (key: Int, value: [Int]) in
return (key, value.count)
}
print(newArray) // prints: [(4, 1), (23, 2), (97, 3)]
#4. Utilisation d'un pour boucle et retour d'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. En utilisant la méthode NSCountedSet
, map
et en retournant une Array
de les tuples (nécessite Fondation)
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. En utilisant NSCountedSet
, AnyIterator
et en retournant un Array
de tuples (nécessite la Fondation)
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édits:
- "1519570920 Swift" Idiomes
- générique sur la Collecte, à l'aide du Dictionnaire
j'ai mis à jour oisdk la réponse de à Swift2.
16/04/14 j'ai mis à jour ce code à Swift2.2
16/10/11 mis à jour en 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
}
}
}
Equatable:
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)]
}
}
}
Utilisation
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)
utiliser un NSCountedSet. Dans L'Objectif C:
NSCountedSet* countedSet = [[NSCountedSet alloc] initWithArray:array];
for (NSString* string in countedSet)
NSLog (@"String %@ occurs %zd times", string, [countedSet countForObject:string]);
je suppose que vous pouvez traduire cela en Swift vous-même.
Que Diriez-vous de:
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]
c'est générique, donc ça marchera avec n'importe quel élément, tant que c'est hachable:
freq([1, 1, 1, 2, 3, 3]) // [2: 1, 3: 2, 1: 3]
freq([true, true, true, false, true]) // [false: 1, true: 4]
et, si vous ne pouvez pas rendre vos éléments hachables, vous pouvez le faire avec tuples:
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)]
j'aime éviter les boucles intérieures et l'utilisation .la carte autant que possible. Donc, si nous avons un tableau de chaîne, nous pouvons faire ce qui suit pour compter les occurrences
var occurances = ["tuples", "are", "awesome", "tuples", "are", "cool", "tuples", "tuples", "tuples", "shades"]
var dict:[String:Int] = [:]
occurances.map{
if let val: Int = dict["151900920"] {
dict["151900920"] = val+1
} else {
dict["151900920"] = 1
}
}
imprime
["tuples": 5, "awesome": 1, "are": 2, "cool": 1, "shades": 1]
une autre approche consisterait à utiliser la méthode du filtre. Je trouve que le plus élégant
var numberOfOccurenses = countedItems.filter(
{
if "151900920" == "FOO" || "151900920" == "BAR" || "151900920" == "FOOBAR" {
return true
}else{
return false
}
}).count
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]
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{ "151900920" }
// ["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{ "151900920".id }
// ["2": 1, "1": 2, "3": 1]
première étape dans le tri de comptage.
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)