Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to count occurrences of an element in a Swift array?

I've seen a few examples of this but all of those seem to rely on knowing which element you want to count the occurrences of. My array is generated dynamically so I have no way of knowing which element I want to count the occurrences of (I want to count the occurrences of all of them). Can anyone advise?

Thanks in advance

EDIT:

Perhaps I should have been clearer, the array will contain multiple different strings (e.g. ["FOO", "FOO", "BAR", "FOOBAR"]

How can I count the occurrences of foo, bar and foobar without knowing what they are in advance?

like image 407
Alex Chesters Avatar asked May 30 '15 11:05

Alex Chesters


People also ask

How do you find the count of an element in an array in Swift?

To get the size of an array in Swift, use count function on the array. Following is a quick example to get the count of elements present in the array. array_name is the array variable name. count returns an integer value for the size of this array.

How do you count occurrence of an element in an array?

To count the occurrences of each element in an array: Declare a variable that stores an empty object. Use the for...of loop to iterate over the array. On each iteration, increment the count for the current element if it exists or initialize the count to 1 .


2 Answers

Swift 3 and Swift 2:

You can use a dictionary of type [String: Int] to build up counts for each of the items in your [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)") } 

output:

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

Swift 4:

Swift 4 introduces (SE-0165) the ability to include a default value with a dictionary lookup, and the resulting value can be mutated with operations such as += and -=, so:

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

becomes:

counts[item, default: 0] += 1 

That makes it easy to do the counting operation in one concise line using 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 introduces a new version of reduce that uses an inout variable to accumulate the results. Using that, the creation of the counts truly becomes a single line:

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] 

Or using the default parameters:

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

Finally you can make this an extension of Sequence so that it can be called on any Sequence containing Hashable items including Array, ArraySlice, String, and String.SubSequence:

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

This idea was borrowed from this question although I changed it to a computed property. Thanks to @LeoDabus for the suggestion of extending Sequence instead of Array to pick up additional types.

Examples:

print("abacab".histogram) 
["a": 3, "b": 2, "c": 1] 
print("Hello World!".suffix(6).histogram) 
["l": 1, "!": 1, "d": 1, "o": 1, "W": 1, "r": 1] 
print([1,2,3,2,1].histogram) 
[2: 2, 3: 1, 1: 2] 
print([1,2,3,2,1,2,1,3,4,5].prefix(8).histogram) 
[1: 3, 2: 3, 3: 2] 
print(stride(from: 1, through: 10, by: 2).histogram) 
[1: 1, 3: 1, 5: 1, 7: 1, 9: 1] 
like image 73
vacawama Avatar answered Oct 07 '22 00:10

vacawama


array.filter{$0 == element}.count 
like image 24
Ruben Avatar answered Oct 07 '22 01:10

Ruben