これのいくつかの例を見てきましたが、それらはすべて、どの要素の出現をカウントしたいかを知ることに依存しているようです。私の配列は動的に生成されるため、発生をカウントする要素を知る方法がありません(それらすべての発生をカウントしたい)。誰でもアドバイスできますか?
前もって感謝します
編集:
おそらく私はもっと明確にすべきだった、配列には複数の異なる文字列が含まれます(例:["FOO", "FOO", "BAR", "FOOBAR"]
Foo、bar、foobarの発生を事前に把握せずにカウントするにはどうすればよいですか?
Swift 3およびSwift 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)
_
Swift 4:
Swift 4 (SE-0165) 辞書ルックアップにデフォルト値を含める機能。結果の値は_+=
_や_-=
_などの操作で変更できます。そう:
_counts[item] = (counts[item] ?? 0) + 1
_
になる:
_counts[item, default: 0] += 1
_
これにより、forEach
を使用して1つの簡潔な行でカウント操作を簡単に行うことができます。
_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
の新しいバージョンが導入されています。それを使用すると、カウントの作成は本当に1行になります。
_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 }
}
}
_
このアイデアは この質問 から借りたものですが、computed propertyに変更しました。
array.filter{$0 == element}.count
Swift 5、必要に応じて、7のPlaygroundサンプルコードのいずれかを選択して、アレイ。
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]
_
repeatElement(_:count:)
関数、Zip(_:_:)
関数、およびDictionary
のinit(_:uniquingKeysWith:)
initializerの使用_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]
_
Dictionary
のinit(grouping:by:)
初期化子と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]
_
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)]
_
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]
_
NSCountedSet
およびNSEnumerator
の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)]
_
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)]
_
クレジット:
oisdk's answer をSwift2に更新しました。
16/04/14このコードをSwift2.2に更新しました
16/10/11がSwift3に更新されました
ハッシュ可能:
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
}
}
}
赤道化可能:
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)
どうですか:
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]
汎用なので、ハッシュ可能な限り、要素が何であれ動作します:
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)]
私は内側のループを避け、可能な限り.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]
NSCountedSetを使用します。 Objective-Cの場合:
NSCountedSet* countedSet = [[NSCountedSet alloc] initWithArray:array];
for (NSString* string in countedSet)
NSLog (@"String %@ occurs %zd times", string, [countedSet countForObject:string]);
これをSwift自分に翻訳できると思います。
他のアプローチは、フィルターメソッドを使用することです。最もエレガントなのは
var numberOfOccurenses = countedItems.filter(
{
if $0 == "FOO" || $0 == "BAR" || $0 == "FOOBAR" {
return true
}else{
return false
}
}).count
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]
この関数を使用して、配列内のアイテムの発生をカウントできます
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)
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]
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]