Подтвердить что ты не робот

Как подсчитать вхождения элемента в массив Swift?

Я видел несколько примеров этого, но все они полагаются на знание того, какой элемент вы хотите подсчитать вхождения. Мой массив генерируется динамически, поэтому у меня нет способа узнать, какой элемент я хочу подсчитать вхождения (я хочу подсчитать вхождения всех из них). Может ли кто-нибудь посоветовать?

Заранее спасибо

EDIT:

Возможно, я должен был быть более ясным, массив будет содержать несколько разных строк (например, ["FOO", "FOO", "BAR", "FOOBAR"]

Как я могу подсчитать вхождения foo, bar и foobar, не зная, что они заблаговременно?

4b9b3361

Ответ 1

Свифт 3 и Свифт 2:

Вы можете использовать словарь типа [String: Int] чтобы увеличить количество элементов для каждого из элементов в [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)")
}

выход:

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

Свифт 4:

Swift 4 представляет (SE-0165) возможность включать значение по умолчанию в словарь поиска, и результирующее значение может быть изменено с помощью таких операций, как += и -=, поэтому:

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

будет выглядеть так:

counts[item, default: 0] += 1

Это позволяет легко выполнить операцию подсчета в одной сжатой строке, используя 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 представляет новую версию reduce которая использует переменную inout для накопления результатов. Используя это, создание счетчиков действительно становится одной строкой:

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]

Или используя параметры по умолчанию:

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

Наконец, вы можете сделать это расширением Array чтобы его можно было вызывать для любого массива, содержащего Hashable элементы:

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

Эта идея была заимствована из этого вопроса, хотя я изменил ее на вычисленную собственность.

Ответ 2

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

Ответ 3

С Swift 5, в соответствии с вашими потребностями, вы можете выбрать один из 7 следующих примеров кодов Playground, чтобы подсчитать вхождения хеш-элементов в массиве.


# 1. Использование Array reduce(into:_:) и 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. Использование функции repeatElement(_:count:) функции zip(_:_:) и Dictionary 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. Использование Dictionary init(grouping:by:) initializer и mapValues(_:)

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. Использование Dictionary init(grouping:by:) инициализатор и метод map(_:)

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. Использование цикла for и Dictionary subscript(_:) индекс 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. Использование NSCountedSet и NSEnumerator map(_:) метод map(_:) (требуется 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. Использование NSCountedSet и AnyIterator (требуется 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)]

Кредиты:

Ответ 4

Я обновил ответ oisdk на Swift2.

16/04/14 Я обновил этот код до Swift2.2

16/10/11 обновлено до 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)]
        }
    }
}

Использование

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)

Ответ 5

Используйте NSCountedSet. В Objective-C:

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

Я предполагаю, что вы можете перевести это в Swift самостоятельно.

Ответ 6

Как насчет:

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]

Он общий, поэтому он будет работать с любым вашим элементом, если он hashable:

freq([1, 1, 1, 2, 3, 3]) // [2: 1, 3: 2, 1: 3]

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

И, если вы не можете сделать ваши хешируемые элементы, вы можете сделать это с помощью кортежей:

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

Ответ 7

Мне нравится избегать внутренних циклов и использовать .map как можно больше. Поэтому, если у нас есть массив строк, мы можем сделать следующее, чтобы подсчитать вхождения

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
    }
}

печатает

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

Ответ 8

Другой подход - использовать метод фильтра. Я нахожу, что самый элегантный

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

Ответ 9

Swift 4

let array = ["FOO", "FOO", "BAR", "FOOBAR"]

// Merging keys with closure for conflicts
let mergedKeysAndValues = Dictionary(zip(array, repeatElement(1, count: array.count)), uniquingKeysWith: +) 

// mergedKeysAndValues is ["FOO": 2, "BAR": 1, "FOOBAR": 1]

Ответ 10

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]

Ответ 11

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]

Ответ 12

Вы можете использовать эту функцию для подсчета количества элементов в массиве

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

Вы можете реализовать это так -

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

Ответ 13

Первый шаг в подсчете сортировки.

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

var countArray = [Int](count: Int(max + 1), repeatedValue: 0)

for num in inputList{
    countArray[num] += 1
}

print(countArray)