Reduce swift array to another one of pairs [duplicate] - arrays

This question already has answers here:
Iterate over collection two at a time in Swift
(8 answers)
Closed 4 years ago.
I have an array of Int's. I need to change it in some way to get the new array of pairs of Int (array of tuples).
let a = [1, 2, 3, 4, 5]
// How to get array b which is
let b: [(Int, Int?)] // And it should be [(1, 2), (3, 4), (5, nil)]
The obvious solution is to use cycles. However I'm wondering whether it is more declarative way. Something like map which takes two arguments
// Something like this
let b = a.tupleMap { ($0, $1) }
UPDATE:
There is an awesome solution in related question.
Based on it I've added my own reduce method:
extension Collection {
func reduceByPairs<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element, Element?) throws -> Result) rethrows -> Result {
return try sequence(state: makeIterator()) { it in
it.next().map { ($0, it.next()) }
}
.reduce(initialResult) {
try nextPartialResult($0, $1.0, $1.1)
}
}
}

I would do something like this.
let array = [1, 2, 3, 4, 5]
let pairedArray = array
.enumerated()
.map { return ($0.element, array.count > $0.offset + 1 ? array[$0.offset + 1] : nil) }
.enumerated()
.filter({ $0.offset % 2 == 0 })
.map { $0.element }
print(pairedArray) // [(1, Optional(2)), (3, Optional(4)), (5, nil)]
Obviously the second element of the tuple is an Optional because you want to be able to have nil when there is no value.

You can try this:
extension Array {
func tupleMap(_ transform: (Element, Element?) -> (Element, Element?) = { ($0,$1) }) -> [(Element, Element?)] {
guard count > 0
else { return [] }
var result = [(Element, Element?)]()
var value1 = self[0]
var value2 : Element? = nil
for i in 0..<count {
if i % 2 == 0 {
value1 = self[i]
}
else {
value2 = self[i]
result.append(transform(value1, value2))
}
}
if count % 2 == 1 {
result.append(transform(value1, nil))
}
return result
}
}
Then you can use it this way:
let b = a.tupleMap { ($0, $1) }
Or even that way:
let b = a.tupleMap()
EDIT :
Or as proposed by Leo here: Iterate over collection two at a time in Swift
Here is a much shorter version:
extension Array {
func tupleMap(_ transform: (Element, Element?) -> (Element, Element?) = { ($0,$1) }) -> [(Element, Element?)] {
return stride(from: 0, to: count, by: 2).map {
transform(self[$0], $0 < count-1 ? self[$0.advanced(by: 1)] : nil)
}
}
}

Related

Array Iteration closure with index in swift -iOS [duplicate]

Is there a function that I can use to iterate over an array and have both index and element, like Python's enumerate?
for index, element in enumerate(list):
...
Yes. As of Swift 3.0, if you need the index for each element along with its value, you can use the enumerated() method to iterate over the array. It returns a sequence of pairs composed of the index and the value for each item in the array. For example:
for (index, element) in list.enumerated() {
print("Item \(index): \(element)")
}
Before Swift 3.0 and after Swift 2.0, the function was called enumerate():
for (index, element) in list.enumerate() {
print("Item \(index): \(element)")
}
Prior to Swift 2.0, enumerate was a global function.
for (index, element) in enumerate(list) {
println("Item \(index): \(element)")
}
Swift 5 provides a method called enumerated() for Array. enumerated() has the following declaration:
func enumerated() -> EnumeratedSequence<Array<Element>>
Returns a sequence of pairs (n, x), where n represents a consecutive integer starting at zero and x represents an element of the sequence.
In the simplest cases, you may use enumerated() with a for loop. For example:
let list = ["Car", "Bike", "Plane", "Boat"]
for (index, element) in list.enumerated() {
print(index, ":", element)
}
/*
prints:
0 : Car
1 : Bike
2 : Plane
3 : Boat
*/
Note however that you're not limited to use enumerated() with a for loop. In fact, if you plan to use enumerated() with a for loop for something similar to the following code, you're doing it wrong:
let list = [Int](1...5)
var arrayOfTuples = [(Int, Int)]()
for (index, element) in list.enumerated() {
arrayOfTuples += [(index, element)]
}
print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
A swiftier way to do this is:
let list = [Int](1...5)
let arrayOfTuples = Array(list.enumerated())
print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]
As an alternative, you may also use enumerated() with map:
let list = [Int](1...5)
let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]
Moreover, although it has some limitations, forEach can be a good replacement to a for loop:
let list = [Int](1...5)
list.reversed().enumerated().forEach { print($0, ":", $1) }
/*
prints:
0 : 5
1 : 4
2 : 3
3 : 2
4 : 1
*/
By using enumerated() and makeIterator(), you can even iterate manually on your Array. For example:
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
var generator = ["Car", "Bike", "Plane", "Boat"].enumerated().makeIterator()
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(type: .system)
button.setTitle("Tap", for: .normal)
button.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
button.addTarget(self, action: #selector(iterate(_:)), for: .touchUpInside)
view.addSubview(button)
}
#objc func iterate(_ sender: UIButton) {
let tuple = generator.next()
print(String(describing: tuple))
}
}
PlaygroundPage.current.liveView = ViewController()
/*
Optional((offset: 0, element: "Car"))
Optional((offset: 1, element: "Bike"))
Optional((offset: 2, element: "Plane"))
Optional((offset: 3, element: "Boat"))
nil
nil
nil
*/
Starting with Swift 2, the enumerate function needs to be called on the collection like so:
for (index, element) in list.enumerate() {
print("Item \(index): \(element)")
}
I found this answer while looking for a way to do that with a Dictionary, and it turns out it's quite easy to adapt it, just pass a tuple for the element.
// Swift 2
var list = ["a": 1, "b": 2]
for (index, (letter, value)) in list.enumerate() {
print("Item \(index): \(letter) \(value)")
}
Swift 5.x:
let list = [0, 1, 2, 3, 4, 5]
list.enumerated().forEach { (index, value) in
print("index: \(index), value: \(value)")
}
Or,
list.enumerated().forEach {
print("index: \($0.offset), value: \($0.element)")
}
Or,
for (index, value) in list.enumerated() {
print("index: \(index), value: \(value)")
}
Swift 5.x:
I personally prefer using the forEach method:
list.enumerated().forEach { (index, element) in
...
}
You can also use the short version:
list.enumerated().forEach { print("index: \($0.0), value: \($0.1)") }
For completeness you can simply iterate over your array indices and use subscript to access the element at the corresponding index:
let list = [100,200,300,400,500]
for index in list.indices {
print("Element at:", index, " Value:", list[index])
}
Using forEach
list.indices.forEach {
print("Element at:", $0, " Value:", list[$0])
}
Using collection enumerated() method. Note that it returns a collection of tuples with the offset and the element:
for item in list.enumerated() {
print("Element at:", item.offset, " Value:", item.element)
}
using forEach:
list.enumerated().forEach {
print("Element at:", $0.offset, " Value:", $0.element)
}
Those will print
Element at: 0 Value: 100
Element at: 1 Value: 200
Element at: 2 Value: 300
Element at: 3 Value: 400
Element at: 4 Value: 500
If you need the array index (not the offset) and its element you can extend Collection and create your own method to get the indexed elements:
extension Collection {
func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
var index = startIndex
for element in self {
try body((index,element))
formIndex(after: &index)
}
}
}
Another possible implementation as suggested by Alex is to zip the collection indices with its elements:
extension Collection {
func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
for element in zip(indices, self) { try body(element) }
}
var indexedElements: Zip2Sequence<Indices, Self> { zip(indices, self) }
}
Testing:
let list = ["100","200","300","400","500"]
// You can iterate the index and its elements using a closure
list.dropFirst(2).indexedElements {
print("Index:", $0.index, "Element:", $0.element)
}
// or using a for loop
for (index, element) in list.indexedElements {
print("Index:", index, "Element:", element)
}
This will p[rint
Index: 2 Element: 300
Index: 3 Element: 400
Index: 4 Element: 500
Index: 0 Element: 100
Index: 1 Element: 200
Index: 2 Element: 300
Index: 3 Element: 400
Index: 4 Element: 500
You can simply use loop of enumeration to get your desired result:
Swift 2:
for (index, element) in elements.enumerate() {
print("\(index): \(element)")
}
Swift 3 & 4:
for (index, element) in elements.enumerated() {
print("\(index): \(element)")
}
Or you can simply go through a for loop to get the same result:
for index in 0..<elements.count {
let element = elements[index]
print("\(index): \(element)")
}
Hope it helps.
Basic enumerate
for (index, element) in arrayOfValues.enumerate() {
// do something useful
}
or with Swift 3...
for (index, element) in arrayOfValues.enumerated() {
// do something useful
}
Enumerate, Filter and Map
However, I most often use enumerate in combination with map or filter. For example with operating on a couple of arrays.
In this array I wanted to filter odd or even indexed elements and convert them from Ints to Doubles. So enumerate() gets you index and the element, then filter checks the index, and finally to get rid of the resulting tuple I map it to just the element.
let evens = arrayOfValues.enumerate().filter({
(index: Int, element: Int) -> Bool in
return index % 2 == 0
}).map({ (_: Int, element: Int) -> Double in
return Double(element)
})
let odds = arrayOfValues.enumerate().filter({
(index: Int, element: Int) -> Bool in
return index % 2 != 0
}).map({ (_: Int, element: Int) -> Double in
return Double(element)
})
Using .enumerate() works, but it does not provide the true index of the element; it only provides an Int beginning with 0 and incrementing by 1 for each successive element. This is usually irrelevant, but there is the potential for unexpected behavior when used with the ArraySlice type. Take the following code:
let a = ["a", "b", "c", "d", "e"]
a.indices //=> 0..<5
let aSlice = a[1..<4] //=> ArraySlice with ["b", "c", "d"]
aSlice.indices //=> 1..<4
var test = [Int: String]()
for (index, element) in aSlice.enumerate() {
test[index] = element
}
test //=> [0: "b", 1: "c", 2: "d"] // indices presented as 0..<3, but they are actually 1..<4
test[0] == aSlice[0] // ERROR: out of bounds
It's a somewhat contrived example, and it's not a common issue in practice but still I think it's worth knowing this can happen.
Starting with Swift 3, it is
for (index, element) in list.enumerated() {
print("Item \(index): \(element)")
}
This is the Formula of loop of Enumeration:
for (index, value) in shoppingList.enumerate() {
print("Item \(index + 1): \(value)")
}
for more detail you can check Here.
For those who want to use forEach.
Swift 4
extension Array {
func forEachWithIndex(_ body: (Int, Element) throws -> Void) rethrows {
try zip((startIndex ..< endIndex), self).forEach(body)
}
}
Or
array.enumerated().forEach { ... }
Xcode 8 and Swift 3:
Array can be enumerated using tempArray.enumerated()
Example:
var someStrs = [String]()
someStrs.append("Apple")
someStrs.append("Amazon")
someStrs += ["Google"]
for (index, item) in someStrs.enumerated()
{
print("Value at index = \(index) is \(item)").
}
console:
Value at index = 0 is Apple
Value at index = 1 is Amazon
Value at index = 2 is Google
For what you are wanting to do, you should use the enumerated() method on your Array:
for (index, element) in list.enumerated() {
print("\(index) - \(element)")
}
Use .enumerated() like this in functional programming:
list.enumerated().forEach { print($0.offset, $0.element) }
In iOS 8.0/Swift 4.0+
You can use forEach
As per the Apple docs:
Returns a sequence of pairs (n, x), where n represents a consecutive integer starting at zero and x represents an element of the sequence.
let numberWords = ["one", "two", "three"]
numberWords.enumerated().forEach { (key, value) in
print("Key: \(key) - Value: \(value)")
}
If you for whatever reason want a more traditional looking for loop that accesses the elements in the array using their index:
let xs = ["A", "B", "C", "D"]
for i in 0 ..< xs.count {
print("\(i) - \(xs[i])")
}
Output:
0 - A
1 - B
2 - C
3 - D
We called enumerate function to implements this. like
for (index, element) in array.enumerate() {
index is indexposition of array
element is element of array
}

Finding indices of max value in swift array

I have 2 arrays. One for players and one for scores. e.g.
var players = ["Bill", "Bob", "Sam", "Dave"]
var scores = [10,15,12,15]
I can find the index of the (first) max score (and the winner's name) by using:
let highScore = scores.max()
let winningPlayerIndex = scores.index(of: highScore!)
let winningPlayer = players[winningPlayerIndex!]
This works fine if there is only one player with the highest score but how would I return multiple indices (i.e. 1 and 3 in this example) for all values that are equal to the max value? I need the indices to then map back to the players array to pull out the names of all the players with the highest score. Or is there a better way to do all of this?
The accepted answer doesn't generalize to comparing computed values on the elements. The simplest and most efficient way to get the min/max value and index is to enumerate the list and work with the tuples (offset, element) instead:
struct Player {
let name: String
let stats: [Double]
}
let found = players.enumerated().max(by: { (a, b) in
battingAvg(a.element.stats) < battingAvg(b.element.stats)
})
print(found.element.name, found.offset) // "Joe", 42
In general you shouldn't rely on comparing floating point values by equality and even where you can, if the computation is expensive you don't want to repeat it to find the item in the list.
What you need is to use custom class or structure and make array of it then find max score and after that filter your array with max score.
struct Player {
let name: String
let score: Int
}
Now create array of this Player structure
var players = [Player(name: "Bill", score: 10), Player(name: "Bob", score: 15), Player(name: "Sam", score: 12), Player(name: "Dave", score: 15)]
let maxScore = players.max(by: { $0.0.score < $0.1.score })?.score ?? 0
To get the array of player with max core use filter on array like this.
let allPlayerWithMaxScore = players.filter { $0.score == maxScore }
To get the array of index for player having high score use filter on array like this.
let indexForPlayerWithMaxScore = players.indices.filter { players[$0].score == maxScore }
print(indexForPlayerWithMaxScore) //[1, 3]
To answer just the question in the title -- find the index of the max value in a (single) array:
extension Array where Element: Comparable {
var indexOfMax: Index? {
guard var maxValue = self.first else { return nil }
var maxIndex = 0
for (index, value) in self.enumerated() {
if value > maxValue {
maxValue = value
maxIndex = index
}
}
return maxIndex
}
}
The extension returns nil if the array is empty. Else, it starts by assuming the first value is the max, iterates over all values, updates the index and value to any larger values found, and finally returns the result.
If you have 2 arrays and need to find max score from first one in order to pull the name from second one, then I would recommend you to convert both arrays into one using zip high order func and retrieve the max value from there.
So having your data it will look like this:
let players = ["Bill", "Bob", "Sam", "Dave"]
let scores = [10,15,12,15]
let data = zip(players, scores)
// max score
let maxResult = data.max(by: ({ $0.1 < $1.1 }))?.1 ?? 0
// outputs 15
// leaders
let leaders = data.filter { $0.1 >= maxResult }.map { "\($0.0) - \($0.1)" }
// outputs ["Bob - 15", "Dave - 15"]
You can zip the collection indices with its elements and get the minimum value using collection min method and pass a predicate to compare the elements. Get the result and extract the index of the tuple:
let numbers = [2, 4, 4, 2, 3, 1]
let minIndex = zip(numbers.indices, numbers).min(by: { $0.1 < $1.1 })?.0 // 5
let maxIndex = zip(numbers.indices, numbers).max(by: { $0.1 < $1.1 })?.0 // 1
As an extension where the elements are comparable:
extension Collection where Element: Comparable {
func firstIndexOfMaxElement() -> Index? {
zip(indices, self).max(by: { $0.1 < $1.1 })?.0
}
func firstIndexOfMinElement() -> Index? {
zip(indices, self).min(by: { $0.1 < $1.1 })?.0
}
}
Usage:
numbers.firstIndexOfMinElement() // 5
If you need to find the maximum or minimum properties:
extension Collection {
func firstIndexOfMaxElement<T: Comparable>(_ predicate: (Element) -> T) -> Index? {
zip(indices, self).max(by: { predicate($0.1) < predicate($1.1) })?.0
}
func firstIndexOfMinElement<T: Comparable>(_ predicate: (Element) -> T) -> Index? {
zip(indices, self).min(by: { predicate($0.1) < predicate($1.1) })?.0
}
}
Usage:
struct Product {
let price: Int
}
let products: [Product] = [.init(price: 2),
.init(price: 4),
.init(price: 4),
.init(price: 2),
.init(price: 3),
.init(price: 1),]
let minPrice = products.firstIndexOfMinElement(\.price) // 5
To return the maximum and minimum elements and their indices:
extension Collection where Element: Comparable {
func maxElementAndIndices() -> (indices: [Index], element: Element)? {
guard let maxValue = self.max() else { return nil }
return (indices.filter { self[$0] == maxValue }, maxValue)
}
func minElementAndIndices() -> (indices: [Index], element: Element)? {
guard let minValue = self.min() else { return nil }
return (indices.filter { self[$0] == minValue }, minValue)
}
}
And the corresponding methods to custom structures/classes:
extension Collection {
func maxElementsAndIndices<T: Comparable>(_ predicate: (Element) -> T) -> [(index: Index, element: Element)] {
guard let maxValue = self.max(by:{ predicate($0) < predicate($1)}) else { return [] }
return zip(indices, self).filter { predicate(self[$0.0]) == predicate(maxValue) }
}
func minElementsAndIndices<T: Comparable>(_ predicate: (Element) -> T) -> [(index: Index, element: Element)] {
guard let minValue = self.min(by:{ predicate($0) < predicate($1)}) else { return [] }
return zip(indices, self).filter { predicate(self[$0.0]) == predicate(minValue) }
}
}
Usage:
let maxNumbers = numbers.maxElementAndIndices() // ([1, 2], element 4)
let minNumbers = numbers.minElementAndIndices() // ([5], element 1)
let maxPriceIndices = products.maxElementsAndIndices(\.price) // [(index: 1, element: Product(price: 4)), (index: 2, element: Product(price: 4))]
let minPriceIndices = products.minElementsAndIndices(\.price) // [(index: 5, element: __lldb_expr_22.Product(price: 1))]
There are a couple of ways to solve your problem, you can solve this by saving the indices of scores.max() and iterate through the players list, and also using then zip function:
var max_score = scores.max()
var players_and_score = zip(players, scores)
for player in players_and_score{
if player.1 == max_score{
print(player.0)
}
}

Find if sequence of elements exists in array

Is it possible to find if a sequence of elements in an array exists?
Lets take some digits from the Pi,
let piDigits=[3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2,7,9,5,0,2,8,8,4,1,9,7,1,6,9,3,9,9,3,7,5,1,0,5,8,2,0,9,7,4,9,4,4]
Now, i want to find if, 5 and 9 exist as sequence elements in the array- in this case they do, once, in positions 4 & 5.
Ideally, i wouldn't like to iterate over the array with a loop, i would like something similar to array.contains(element) .
#Bawpotter, the code snippet:
for element in piDigits{ //check every element
if element == 5 { //if element is equal with the element i want
var currentPosition = piDigits.index(of: element) //get the position of that element
if piDigits[currentPosition!+1] == 9 { //if the element at the next position is equal to the other element i want
print("true") // it prints true 7 times, instead of 1!
}
}
}
You can filter your indices where its subsequence elementsEqual is true:
extension Collection where Element: Equatable {
func firstIndex<C: Collection>(of collection: C) -> Index? where C.Element == Element {
guard !collection.isEmpty else { return nil }
let size = collection.count
return indices.dropLast(size-1).first {
self[$0..<index($0, offsetBy: size)].elementsEqual(collection)
}
}
func indices<C: Collection>(of collection: C) -> [Index] where C.Element == Element {
guard !collection.isEmpty else { return [] }
let size = collection.count
return indices.dropLast(size-1).filter {
self[$0..<index($0, offsetBy: size)].elementsEqual(collection)
}
}
func range<C: Collection>(of collection: C) -> Range<Index>? where C.Element == Element {
guard !collection.isEmpty else { return nil }
let size = collection.count
var range: Range<Index>!
guard let _ = indices.dropLast(size-1).first(where: {
range = $0..<index($0, offsetBy: size)
return self[range].elementsEqual(collection)
}) else {
return nil
}
return range
}
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
guard !collection.isEmpty else { return [] }
let size = collection.count
return indices.dropLast(size-1).compactMap {
let range = $0..<index($0, offsetBy: size)
return self[range].elementsEqual(collection) ? range : nil
}
}
}
[1, 2, 3, 1, 2].indices(of: [1,2]) // [0,3]
[1, 2, 3, 1, 2].ranges(of: [1,2]) // [[0..<2], [3..<5]]
If you only need to check if a collection contains a subsequence:
extension Collection where Element: Equatable {
func contains<C: Collection>(_ collection: C) -> Bool where C.Element == Element {
guard !collection.isEmpty else { return false }
let size = collection.count
for i in indices.dropLast(size-1) where self[i..<index(i, offsetBy: size)].elementsEqual(collection) {
return true
}
return false
}
}
[1, 2, 3].contains([1, 2]) // true
A very simple implementation using linear search:
let piDigits: [Int] = [3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2,7,9,5,0,2,8,8,4,1,9,7,1,6,9,3,9,9,3,7,5,1,0,5,8,2,0,9,7,4,9,4,4]
let searchedSequence: [Int] = [5, 9]
var index = 0
var resultIndices: [Int] = []
while index < (piDigits.count - searchedSequence.count) {
let subarray = piDigits[index ..< (index + searchedSequence.count)]
if subarray.elementsEqual(searchedSequence) {
resultIndices.append(index)
}
index += 1
}
print("Result: \(resultIndices)")
There are other variants as well, you could, for example, keep dropping the first character from piDigits during iteration and check whether piDigits start with the searchedSequence.
If performance is critical, I recommend using a string searching algorithm, e.g. Aho-Corasick (see https://en.wikipedia.org/wiki/String_searching_algorithm) which builds a state machine first for fast comparison (similar to regular expressions).
Let's see how regular expressions can be used:
let searchedSequences: [[Int]] = [[5, 9], [7], [9, 2]]
let stringDigits = piDigits.map { String($0) }.joined()
let stringSearchedSequences = searchedSequences.map { sequence in sequence.map { String($0) }.joined() }
let regularExpressionPattern = stringSearchedSequences.joined(separator: "|")
let regularExpression = try! NSRegularExpression(pattern: regularExpressionPattern, options: [])
let matches = regularExpression.matches(in: stringDigits, options: [], range: NSRange(location: 0, length: stringDigits.characters.count))
let matchedIndices = matches.map { $0.range.location }
print("Matches: \(matchedIndices)")
The downside of the approach is that it won't search overlapping ranges (e.g. "592" matches two ranges but only one is reported).
Inside the contains method iterates over the array and here you have to do the same thing. Here an example:
extension Array where Element: Equatable {
func contains(array elements: [Element]) -> Int {
guard elements.count > 0 else { return 0 }
guard count > 0 else { return -1 }
var ti = 0
for (index, element) in self.enumerated() {
ti = elements[ti] == element ? ti + 1 : 0
if ti == elements.count {
return index - elements.count + 1
}
}
return -1
}
}
And here how to use it:
let index = [1, 4, 5, 6, 6, 9, 6, 8, 10, 3, 4].contains(array: [6, 8, 10])
// index = 6
let index = [1, 4, 5, 6, 6, 9, 6, 8, 10, 3, 4].contains(array: [6, 8, 1])
// index = -1
let firstSeqNum = 5
let secondSeqNum = 9
for (index, number) in array.enumerated() {
if number == firstSeqNum && array[index+1] == secondSeqNum {
print("The sequence \(firstSeqNum), \(secondSeqNum) was found, starting at an index of \(index).")
}
}
Since there's no built-in method for this, this would be your best option.

Getting the most frequent value of an array

I have an Array of numbers and I want to know which number is most frequent in this array. The array sometimes has 5-6 integers, sometimes it has 10-12, sometimes even more - also the integers in the array can be different. So I need a function which can work with different lengths and values of an array.
One example:
myArray = [0, 0, 0, 1, 1]
Another example:
myArray = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]
Now I am searching for a function which gives out 0 (in the first example) as Integer, as it is 3 times in this array and the other integer in the array (1) is only 2 times in the array. Or for the second example it would be 4.
It seems pretty simple, but I cannot find a solution for this. Found some examples in the web, where the solution is to work with dictionaries or where the solution is simple - but I cannot use it with Swift 3 it seems...
However, I did not find a solution which works for me. Someone has an idea how to get the most frequent integer in an array of integers?
You can also use the NSCountedSet, here's the code
let nums = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]
let countedSet = NSCountedSet(array: nums)
let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
Thanks to #Ben Morrow for the smart suggestions in the comments below.
let myArray = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]
// Create dictionary to map value to count
var counts = [Int: Int]()
// Count the values with using forEach
myArray.forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
// Find the most frequent value and its count with max(by:)
if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
print("\(value) occurs \(count) times")
}
Output:
4 occurs 4 times
Here it is as a function:
func mostFrequent(array: [Int]) -> (value: Int, count: Int)? {
var counts = [Int: Int]()
array.forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
return (value, count)
}
// array was empty
return nil
}
if let result = mostFrequent(array: [1, 3, 2, 1, 1, 4, 5]) {
print("\(result.value) occurs \(result.count) times")
}
1 occurs 3 times
Update for Swift 4:
Swift 4 introduces reduce(into:_:) and default values for array look ups which enable you to generate the frequencies in one efficient line. And we might as well make it generic and have it work for any type that is Hashable:
func mostFrequent<T: Hashable>(array: [T]) -> (value: T, count: Int)? {
let counts = array.reduce(into: [:]) { $0[$1, default: 0] += 1 }
if let (value, count) = counts.max(by: { $0.1 < $1.1 }) {
return (value, count)
}
// array was empty
return nil
}
if let result = mostFrequent(array: ["a", "b", "a", "c", "a", "b"]) {
print("\(result.value) occurs \(result.count) times")
}
a occurs 3 times
The most frequent value is called the "mode". Here's a concise version:
let mode = myArray.reduce([Int: Int]()) {
var counts = $0
counts[$1] = ($0[$1] ?? 0) + 1
return counts
}.max { $0.1 < $1.1 }?.0
Whether that's considered "unreadable" or "elegant" depends on your feelings towards higher order functions. Nonetheless, here it is as a generic method in an extension on Array (so it'll work with any Hashable element type):
extension Array where Element: Hashable {
var mode: Element? {
return self.reduce([Element: Int]()) {
var counts = $0
counts[$1] = ($0[$1] ?? 0) + 1
return counts
}.max { $0.1 < $1.1 }?.0
}
}
Simply remove the .0 if you'd rather have a tuple that includes the count of the mode.
My take on it with Swift 5:
extension Collection {
/**
Returns the most frequent element in the collection.
*/
func mostFrequent() -> Self.Element?
where Self.Element: Hashable {
let counts = self.reduce(into: [:]) {
return $0[$1, default: 0] += 1
}
return counts.max(by: { $0.1 < $1.1 })?.key
}
}
I have tried the following code. It helps especially when the max count is applicable for 2 or more values.
var dictionary = arr.reduce(into: [:]) { counts, number in counts[number, default: 0] += 1}
var max = dictionary.values.max()!
dictionary = dictionary.filter{$0.1 == max}
mode = dictionary.keys.min()!
func mostR(num : [Int]) -> (number : Int , totalRepeated : Int)
{
var numberTofind : Int = 0
var total : Int = 0
var dic : [Int : Int] = [:]
for index in num
{
if let count = dic[index]
{
dic[index] = count + 1
}
else
{
dic[index] = 1
}
}
var high = dic.values.max()
for (index , count) in dic
{
if dic[index] == high
{
numberTofind = index
top.append(count)
total = count
}
}
return (numberTofind , total)
}
var array = [1,22,33,55,4,3,2,0,0,0,0]
var result = mostR(num : [1,22,3,2,43,2,11,0,0,0])
print("the number is (result.number) and its repeated by :(result.totalRepeated)" )
Here is an encapsulated/reusable method.
extension Array where Element: Hashable {
/// The mode will be nil when the array is empty.
var mode: Element? {
var counts: [Element: Int] = [:]
forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
print("\(value) occurs \(count) times")
return value
} else {
return nil
}
}
}
usage:
print([3, 4, 5, 6, 6].mode) // 6
Keep track of each occurrence, counting the value of each key in a dictionary. This case is exclusive for integers. Will update this method using generics.
func mostCommon(of arr: [Int]) -> Int {
var dict = [Int:Int]()
arr.forEach {
if let count = dict[$0] {
dict[$0] = count + 1
} else {
dict[$0] = 1
}
}
let max = dict.values.max()
for (_ , value) in dict {
if value == max {
return value
}
}
return -1
}

count numbers in array and order them by count in swift

Is there a easy way to sort an array by the count of numbers? And if a number have the same count put the highest number first.
[2,8,2,6,1,8,2,6,6]
to
[6,6,6,2,2,2,8,8,1]
What you are looking for is a way to get the frequencies of values.
As long as the values are Hashable this function will work:
It extends all sequence types where the Element is Hashable, so an array of Int will work.
extension SequenceType where Generator.Element : Hashable {
func frequencies() -> [Generator.Element:Int] {
var results : [Generator.Element:Int] = [:]
for element in self {
results[element] = (results[element] ?? 0) + 1
}
return results
}
}
Then you can do this:
let alpha = [2,8,2,6,1,8,2,6,6]
let sorted = alpha.frequencies().sort {
if $0.1 > $1.1 { // if the frequency is higher, return true
return true
} else if $0.1 == $1.1 { // if the frequency is equal
return $0.0 > $1.0 // return value is higher
} else {
return false // else return false
}
}
Even better, you can now create another extension to sequence types.
Now they need to conform to Comparable as well as Hashable
extension SequenceType where Generator.Element : protocol<Hashable,Comparable> {
func sortByFrequency() -> [Generator.Element] {
// the same sort function as before
let sorted = self.frequencies().sort {
if $0.1 > $1.1 {
return true
} else if $0.1 == $1.1 {
return $0.0 > $1.0
} else {
return false
}
}
// this is to convert back from the dictionary to an array
var sortedValues : [Generator.Element] = []
sorted.forEach { // for each time the value was found
for _ in 0..<$0.1 {
sortedValues.append($0.0) // append
}
}
return sortedValues
}
}
Your final usage of all this will look like this :
let sorted = alpha.sortByFrequency() // [6, 6, 6, 2, 2, 2, 8, 8, 1]
Super clean :)
If you prefer a function closer to sort itself you can also use this :
extension SequenceType where Generator.Element : Hashable {
func sortedFrequency(#noescape isOrderedBefore: ((Self.Generator.Element,Int), (Self.Generator.Element,Int)) -> Bool) -> [Generator.Element] {
let sorted = self.frequencies().sort {
return isOrderedBefore($0,$1) // this uses the closure to sort
}
var sortedValues : [Generator.Element] = []
sorted.forEach {
for _ in 0..<$0.1 {
sortedValues.append($0.0)
}
}
return sortedValues
}
}
The extension above converts the array to a frequency dictionary internally and just asks you to input a closure that returns a Bool. Then you can apply different sorting depending on your needs.
Because you pass the closure with the sorting logic to this function the Elements of the SequenceType no longer need to be comparable.
Cheat sheet for all the shorthand:
$0 // first element
$1 // second element
$0.0 // value of first element
$0.1 // frequency of first element
Sorting :
let sortedB = alpha.sortedFrequency {
if $0.1 > $1.1 {
return true
} else if $0.1 == $1.1 {
return $0.0 > $1.0
} else {
return false
}
} // [6, 6, 6, 2, 2, 2, 8, 8, 1]
I'm not sure if this is the most efficient way to do it, but I think it is fairly elegant:
extension Array where Element: Equatable {
func subArrays() -> [[Element]] {
if self.isEmpty {
return [[]]
} else {
let slice = self.filter { $0 == self[0] }
let rest = self.filter { $0 != self[0] }
return rest.isEmpty
? [slice]
: [slice] + rest.subArrays()
}
}
func sortByFrequency(secondarySort: ((Element, Element) -> Bool)? = nil) -> [Element] {
return self.subArrays()
.sort { secondarySort?($0[0], $1[0]) ?? false }
.sort { $0.count > $1.count }
.flatMap { $0 }
}
}
let nums = [2,8,2,6,1,8,2,6,6]
print(nums.sortByFrequency(>)) // [6, 6, 6, 2, 2, 2, 8, 8, 1]
The function subArrays just breaks the array down into an array of sub-arrays for each value in the original array - i.e., you'd get [[2,2,2],[8,8],[6,6,6],[1]] for the input that you provided.
sortByFrequency sorts the output of subArrays and then flatMaps to get the answer.
EDIT: I modified sortByFrequency to add the optional secondarySearch parameter. That allows you to control how you want items that occur at the same frequency to be sorted. Or, just accept the default nil and they won't be sorted by anything other than frequency.
Also, I modified the extension to indicate that Element only needs to conform to Equatable, not Comparable.
//: Playground - noun: a place where people can play
import UIKit
var arr1 = [2,8,2,6,1,8,2,6,6]
var arr2 = [6,6,6,2,2,2,8,8,1]
var counting = [Int: Int]()
// fill counting dictionary
for num in arr1 {
if counting[num] != nil {
counting[num]!++
} else {
counting[num] = 1
}
}
// [6: 3, 2: 3, 8: 2, 1: 1]
print(counting)
func order(i1: Int, i2: Int) -> Bool {
let count1 = counting[i1]
let count2 = counting[i2]
// if counting is the same: compare which number is greater
if count1 == count2 {
return i1 > i2
} else {
return count1 > count2
}
}
// [6, 6, 6, 2, 2, 2, 8, 8, 1]
print(arr1.sort(order))
print(arr2)
Using grouping in Dictionary:
var entries = [1,2,3,3,1,3,5,6,3,4,1,5,5,5,5]
extension Sequence where Element : Hashable {
func byFrequency() -> [Element] {
Dictionary(grouping: self, by: {$0}).sorted{ (a, b) in
a.value.count > b.value.count
}.map { $0.key}
}
}
print(entries.byFrequency().first)
Prints 5

Resources