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?

31
demandé sur Juan Catalan 2015-05-30 14:21:24

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é .

71
répondu vacawama 2018-04-23 12:01:32
array.filter{"151900920" == element}.count
45
répondu Ruben Zilibowitz 2016-10-07 07:22:25

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:

13
répondu Imanou Petit 2018-01-31 13:10:19

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)
9
répondu ken0nek 2017-05-23 12:26:33

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.

3
répondu gnasher729 2015-05-30 11:34:31

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)]
3
répondu oisdk 2015-05-30 11:50:25

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]
3
répondu EmilDo 2017-02-18 09:29:07

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
1
répondu user1700737 2015-05-31 12:55:27

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]
0
répondu VictorLi 2017-09-28 02:14:23
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]
0
répondu Carlos Andres Chaguendo 2018-08-24 17:04:16

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)
-1
répondu Abo3atef 2016-12-08 10:50:58