Swift: algorithm to append items in array - arrays

I need to append items to an array using a specific algorithm:
with start index == 2 and separator == 5
for example input array:
["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]
for example output array:
["1", "1", "X", "1", "1", "1", "1", "1", "X", "1", "1", "1", "1", "1", "X", "1", "1", "1", "1"]
here is my code, but I can not find a way to make it work properly, looking forward to some help
var mixStartIndex = 2
var mixSeparator = 5
let array = ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]
var result = [AnyObject]()
for (index, item) in array.enumerated() {
if (index > mixStartIndex+mixSeparator && index % mixSeparator == 0) || index == mixStartIndex {
result.append("X" as AnyObject)
}
result.append(item as AnyObject)
}
print(result)
PS I am working with objects, just made example with strings for simplicity

You are missing to append the input array as a part of the output array
Try adding this
else
{
result.append(array[index])
}
Update :
changing the condition to if (((index-mixStartIndex)%mixSeparator)==0 || index == mixStartIndex) works for the fifth index
var mixStartIndex = 2
var mixSeparator = 6
func mix(array: [String]) -> [String] {
guard !array.isEmpty else { return array }
var result = [String]()
for (index, _) in array.enumerated() {
if (((index-mixStartIndex)%mixSeparator)==0 || index == mixStartIndex) {
result.append("X")
}
else
{
result.append(array[index])
}
}
return result
}

This works and is simpler than the accepted answer:
extension Array {
func example(start: Index, seperator: Index, insert: Element) -> [Element] {
var result = self
var index = start
while index < result.count {
result.insert(insert, at: index)
index += seperator + 1
}
return result
}
// a functional solution
func example2(start: Index, seperator: Index, insert: Element) -> [Element] {
return self.enumerated().flatMap { offset, element in
offset == start || offset > start && (offset - start) % seperator == 0 ? [insert, element] : [element]
}
}
}
print(array.example(start: mixStartIndex, seperator: mixSeparator, insert: "X"))
print(array.example2(start: mixStartIndex, seperator: mixSeparator, insert: "X"))

Here is another one, a little more straightforward.
let array = ["1","1","1","1","1","1","1","1","1","1","1","1","1","1","1"]
let result = array.enumerated().flatMap{ ($0.offset - 1) % 5 == 0 ? [$0.element, "X"] : [$0.element]}
print(result)

A functional solution to create an array where elements are inserted into the resulting array:
let result: [String] = array.enumerated().reduce(into:[]) { acc, elm in
let tempo = ((elm.offset - 2) % 5 == 0 ? ["X"] : []) + [elm.element]
acc.append(contentsOf: tempo)
}
If instead of inserting you'd like to replace the elements :
let result = array.enumerated().map {
($0.offset - 2) % 6 == 0 ? "X" : $0.element
}
Array.insert(_:at:)
We could use the built-in function Array.insert(_:at:)
let newCount = array.count > 2 ? array.count + 1 + (array.count - 3) / 6 : array.count
var index = 2
let mixSeparator = 6
var result = array
while index < newCount {
result.insert("X", at: index)
index += mixSeparator
}
But that would be inefficient, not only because you the whole array and then start inserting, but also because insert(_:at:) is an O(n) operation. Meaning that all the elements after the index of insertion should be shifted. A more efficient solution would be to do the insertion iteratively :
Iterative insert
Here is a pretty easy to understand iterative solution :
let array = ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]
var result = array
let x = "X"
let startIndex = 2
let step = 6
var count = 0
var index = 2
while index < array.count {
result[index] = x
count += 1
index += step
}
result.append(contentsOf: Array(repeating: "1", count: count))
Or, if you'd like to create the result array in one go :
let newCount = array.count > 2 ? array.count + 1 + (array.count - 3) / 6 : array.count
var result = [String]()
result.reserveCapacity(newCount)
let startIndex = 2
let step = 6
var nextIndexToPutX = startIndex
var index = 0
while index < newCount {
if index == nextIndexToPutX {
result.append("X")
nextIndexToPutX += step
} else {
result.append("1")
}
index += 1
}
Replace iteratively
var result = [String]()
result.reserveCapacity(array.count)
var nextIndexToPutX = 2
let step = 6
var index = 0
while index < array.count {
if index == nextIndexToPutX {
result.append("X")
nextIndexToPutX += step
} else {
result.append(array[index])
}
index += 1
}

Related

How can I remove duplicate strings from an array and count them in Swift?

want to print out duplicate characters first and the next number of characters.
like from ["a","a","b","c","c"] to ["a", "2","c","2"].
However, I properly didn't solve this. want you to give me feedback on my code or let me know your wisdom how can you solve this.
import Foundation
var m : [String] = ["a","a","b","c","c"]
var count : Int = 0
var result : [String] = []
for i in 0..<m.count{
for word in m {
if m[i] == word{
count += 1
result.append(word)
}else{
count = 0
}
if count > 1{
result.append(String(count))
}
}
}
print("\(count)")
print("\(result)")
//["a", "a", "2", "a", "a", "2", "b", "c", "c", "2", "c", "c", "2"]
print(["a", "2","c","2"])// want to print it on the console like this.
There is a type for it, NSCountedSet
let m = ["a","a","b","c","c"]
let countedSet = NSCountedSet(array: m)
var result = [String]()
countedSet.forEach {
let count = countedSet.count(for: $0)
if count > 1 {
result.append(contentsOf: [$0 as! String, String(count)])
}
}
print(result)
You can get directly get duplicate value by below method
let fiteredValue = arrayList.filterDuplicates(includeElement: {$0 == $1})

How can I partition a [String] into a specified number of equal parts in Swift? [duplicate]

Starting with a large [String] and a given subarray size, what is the best way I could go about splitting up this array into smaller arrays? (The last array will be smaller than the given subarray size).
Concrete example:
Split up ["1","2","3","4","5","6","7"] with max split size 2
The code would produce [["1","2"],["3","4"],["5","6"],["7"]]
Obviously I could do this a little more manually, but I feel like in swift something like map() or reduce() may do what I want really beautifully.
In Swift 3/4 this would look like the following:
let numbers = ["1","2","3","4","5","6","7"]
let chunkSize = 2
let chunks = stride(from: 0, to: numbers.count, by: chunkSize).map {
Array(numbers[$0..<min($0 + chunkSize, numbers.count)])
}
// prints as [["1", "2"], ["3", "4"], ["5", "6"], ["7"]]
As an extension to Array:
extension Array {
func chunked(by chunkSize: Int) -> [[Element]] {
return stride(from: 0, to: self.count, by: chunkSize).map {
Array(self[$0..<Swift.min($0 + chunkSize, self.count)])
}
}
}
Or the slightly more verbose, yet more general:
let numbers = ["1","2","3","4","5","6","7"]
let chunkSize = 2
let chunks: [[String]] = stride(from: 0, to: numbers.count, by: chunkSize).map {
let end = numbers.endIndex
let chunkEnd = numbers.index($0, offsetBy: chunkSize, limitedBy: end) ?? end
return Array(numbers[$0..<chunkEnd])
}
This is more general because I am making fewer assumptions about the type of the index into the collection. In the previous implementation I assumed that they could be could be compared and added.
Note that in Swift 3 the functionality of advancing indices has been transferred from the indices themselves to the collection.
With Swift 5, according to your needs, you can choose one of the five following ways in order to solve your problem.
1. Using AnyIterator in a Collection extension method
AnyIterator is a good candidate to iterate over the indices of an object that conforms to Collection protocol in order to return subsequences of this object. In a Collection protocol extension, you can declare a chunked(by:) method with the following implementation:
extension Collection {
func chunked(by distance: Int) -> [[Element]] {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
var index = startIndex
let iterator: AnyIterator<Array<Element>> = AnyIterator({
let newIndex = self.index(index, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex
defer { index = newIndex }
let range = index ..< newIndex
return index != self.endIndex ? Array(self[range]) : nil
})
return Array(iterator)
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
2. Using stride(from:to:by:) function in an Array extension method
Array indices are of type Int and conform to Strideable protocol. Therefore, you can use stride(from:to:by:) and advanced(by:) with them. In an Array extension, you can declare a chunked(by:) method with the following implementation:
extension Array {
func chunked(by distance: Int) -> [[Element]] {
let indicesSequence = stride(from: startIndex, to: endIndex, by: distance)
let array: [[Element]] = indicesSequence.map {
let newIndex = $0.advanced(by: distance) > endIndex ? endIndex : $0.advanced(by: distance)
//let newIndex = self.index($0, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex // also works
return Array(self[$0 ..< newIndex])
}
return array
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
3. Using a recursive approach in an Array extension method
Based on Nate Cook recursive code, you can declare a chunked(by:) method in an Array extension with the following implementation:
extension Array {
func chunked(by distance: Int) -> [[Element]] {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
if self.count <= distance {
return [self]
} else {
let head = [Array(self[0 ..< distance])]
let tail = Array(self[distance ..< self.count])
return head + tail.chunked(by: distance)
}
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
4. Using a for loop and batches in a Collection extension method
Chris Eidhof and Florian Kugler show in Swift Talk #33 - Sequence & Iterator (Collections #2) video how to use a simple for loop to fill batches of sequence elements and append them on completion to an array. In a Sequence extension, you can declare a chunked(by:) method with the following implementation:
extension Collection {
func chunked(by distance: Int) -> [[Element]] {
var result: [[Element]] = []
var batch: [Element] = []
for element in self {
batch.append(element)
if batch.count == distance {
result.append(batch)
batch = []
}
}
if !batch.isEmpty {
result.append(batch)
}
return result
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
5. Using a custom struct that conforms to Sequence and IteratorProtocol protocols
If you don't want to create extensions of Sequence, Collection or Array, you can create a custom struct that conforms to Sequence and IteratorProtocol protocols. This struct should have the following implementation:
struct BatchSequence<T>: Sequence, IteratorProtocol {
private let array: [T]
private let distance: Int
private var index = 0
init(array: [T], distance: Int) {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
self.array = array
self.distance = distance
}
mutating func next() -> [T]? {
guard index < array.endIndex else { return nil }
let newIndex = index.advanced(by: distance) > array.endIndex ? array.endIndex : index.advanced(by: distance)
defer { index = newIndex }
return Array(array[index ..< newIndex])
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let batchSequence = BatchSequence(array: array, distance: 2)
let newArray = Array(batchSequence)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
I wouldn't call it beautiful, but here's a method using map:
let numbers = ["1","2","3","4","5","6","7"]
let splitSize = 2
let chunks = numbers.startIndex.stride(to: numbers.count, by: splitSize).map {
numbers[$0 ..< $0.advancedBy(splitSize, limit: numbers.endIndex)]
}
The stride(to:by:) method gives you the indices for the first element of each chunk, so you can map those indices to a slice of the source array using advancedBy(distance:limit:).
A more "functional" approach would simply be to recurse over the array, like so:
func chunkArray<T>(s: [T], splitSize: Int) -> [[T]] {
if countElements(s) <= splitSize {
return [s]
} else {
return [Array<T>(s[0..<splitSize])] + chunkArray(Array<T>(s[splitSize..<s.count]), splitSize)
}
}
I like Nate Cook's answer, it looks like Swift has moved on since it was written, here's my take on this as an extension to Array:
extension Array {
func chunk(chunkSize : Int) -> Array<Array<Element>> {
return 0.stride(to: self.count, by: chunkSize)
.map { Array(self[$0..<$0.advancedBy(chunkSize, limit: self.count)]) }
}
}
Note, it returns [] for negative numbers and will result in a fatal error as written above. You'll have to put a guard in if you want to prevent that.
func testChunkByTwo() {
let input = [1,2,3,4,5,6,7]
let output = input.chunk(2)
let expectedOutput = [[1,2], [3,4], [5,6], [7]]
XCTAssertEqual(expectedOutput, output)
}
func testByOne() {
let input = [1,2,3,4,5,6,7]
let output = input.chunk(1)
let expectedOutput = [[1],[2],[3],[4],[5],[6],[7]]
XCTAssertEqual(expectedOutput, output)
}
func testNegative() {
let input = [1,2,3,4,5,6,7]
let output = input.chunk(-2)
let expectedOutput = []
XCTAssertEqual(expectedOutput, output)
}
I don't think you'll want to use map or reduce. Map is for applying a function on each individual element in an array while reduce is for flattening an array. What you want to do is slice the array into subarrays of a certain size. This snippet uses slices.
var arr = ["1","2","3","4","5","6","7"]
var splitSize = 2
var newArr = [[String]]()
var i = 0
while i < arr.count {
var slice: Slice<String>!
if i + splitSize >= arr.count {
slice = arr[i..<arr.count]
}
else {
slice = arr[i..<i+splitSize]
}
newArr.append(Array(slice))
i += slice.count
}
println(newArr)
Would be nice to express Tyler Cloutier's formulation as an extension on Array:
extension Array {
func chunked(by chunkSize:Int) -> [[Element]] {
let groups = stride(from: 0, to: self.count, by: chunkSize).map {
Array(self[$0..<[$0 + chunkSize, self.count].min()!])
}
return groups
}
}
This gives us a general way to partition an array into chunks.
New in Swift 4, you can do this efficiently with reduce(into:). Here's an extension on Sequence:
extension Sequence {
func eachSlice(_ clump:Int) -> [[Self.Element]] {
return self.reduce(into:[]) { memo, cur in
if memo.count == 0 {
return memo.append([cur])
}
if memo.last!.count < clump {
memo.append(memo.removeLast() + [cur])
} else {
memo.append([cur])
}
}
}
}
Usage:
let result = [1,2,3,4,5,6,7,8,9].eachSlice(2)
// [[1, 2], [3, 4], [5, 6], [7, 8], [9]]
The above is very cleaver, but it makes my head hurt. I had to revert back to a less swifty approach.
For Swift 2.0
var chunks = [[Int]]()
var temp = [Int]()
var splitSize = 3
var x = [1,2,3,4,5,6,7]
for (i, element) in x.enumerate() {
if temp.count < splitSize {
temp.append(element)
}
if temp.count == splitSize {
chunks.append(temp)
temp.removeAll()
}
}
if !temp.isEmpty {
chunks.append(temp)
}
Playground Result [[1, 2, 3], [4, 5, 6], [7]]
I'll just throw my hat in the ring here with another implementation based on AnyGenerator.
extension Array {
func chunks(_ size: Int) -> AnyIterator<[Element]> {
if size == 0 {
return AnyIterator {
return nil
}
}
let indices = stride(from: startIndex, to: count, by: size)
var generator = indices.makeIterator()
return AnyIterator {
guard let i = generator.next() else {
return nil
}
var j = self.index(i, offsetBy: size)
repeat {
j = self.index(before: j)
} while j >= self.endIndex
return self[i...j].lazy.map { $0 }
}
}
}
I prefer this method since it relies exclusively on generators which can have a non-negligible, positive memory impact when dealing with large arrays.
For your specific example, here's how it would work:
let chunks = Array(["1","2","3","4","5","6","7"].chunks(2))
Result:
[["1", "2"], ["3", "4"], ["5", "6"], ["7"]]
In Swift 4 or later you can also extend Collection and return a collection of SubSequence of it to be able to use it also with StringProtocol types (String or Substring). This way it will return a collection of substrings instead of a collection of a bunch of characters:
Xcode 10.1 • Swift 4.2.1 or later
extension Collection {
func subSequences(limitedTo maxLength: Int) -> [SubSequence] {
precondition(maxLength > 0, "groups must be greater than zero")
var start = startIndex
var subSequences: [SubSequence] = []
while start < endIndex {
let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
defer { start = end }
subSequences.append(self[start..<end])
}
return subSequences
}
}
Or as suggested in comments by #Jessy using collection method
public func sequence<T, State>(state: State, next: #escaping (inout State) -> T?) -> UnfoldSequence<T, State>
extension Collection {
func subSequences(limitedTo maxLength: Int) -> [SubSequence] {
precondition(maxLength > 0, "groups must be greater than zero")
return .init(sequence(state: startIndex) { start in
guard start < self.endIndex else { return nil }
let end = self.index(start, offsetBy: maxLength, limitedBy: self.endIndex) ?? self.endIndex
defer { start = end }
return self[start..<end]
})
}
}
Usage
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let slices = array.subSequences(limitedTo: 2) // [ArraySlice(["1", "2"]), ArraySlice(["3", "4"]), ArraySlice(["5", "6"]), ArraySlice(["7", "8"]), ArraySlice(["9"])]
for slice in slices {
print(slice) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
}
// To convert from ArraySlice<Element> to Array<element>
let arrays = slices.map(Array.init) // [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
extension Collection {
var singles: [SubSequence] { return subSequences(limitedTo: 1) }
var pairs: [SubSequence] { return subSequences(limitedTo: 2) }
var triples: [SubSequence] { return subSequences(limitedTo: 3) }
var quads: [SubSequence] { return subSequences(limitedTo: 4) }
}
Array or ArraySlice of Characters
let chars = ["a","b","c","d","e","f","g","h","i"]
chars.singles // [["a"], ["b"], ["c"], ["d"], ["e"], ["f"], ["g"], ["h"], ["i"]]
chars.pairs // [["a", "b"], ["c", "d"], ["e", "f"], ["g", "h"], ["i"]]
chars.triples // [["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]]
chars.quads // [["a", "b", "c", "d"], ["e", "f", "g", "h"], ["i"]]
chars.dropFirst(2).quads // [["c", "d", "e", "f"], ["g", "h", "i"]]
StringProtocol Elements (String and SubString)
let str = "abcdefghi"
str.singles // ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
str.pairs // ["ab", "cd", "ef", "gh", "i"]
str.triples // ["abc", "def", "ghi"]
str.quads // ["abcd", "efgh", "i"]
str.dropFirst(2).quads // ["cdef", "ghi"]
Swift 5.1 - General solution for all kind of Collections:
extension Collection where Index == Int {
func chunked(by chunkSize: Int) -> [[Element]] {
stride(from: startIndex, to: endIndex, by: chunkSize).map { Array(self[$0..<Swift.min($0 + chunkSize, count)]) }
}
}
Do you know that any solution with [a...b] swift style works 10 times slower then regular?
for y in 0..<rows {
var row = [Double]()
for x in 0..<cols {
row.append(stream[y * cols + x])
}
mat.append(row)
}
Try it and will see, here is my raw code for test:
let count = 1000000
let cols = 1000
let rows = count / cols
var stream = [Double].init(repeating: 0.5, count: count)
// Regular
var mat = [[Double]]()
let t1 = Date()
for y in 0..<rows {
var row = [Double]()
for x in 0..<cols {
row.append(stream[y * cols + x])
}
mat.append(row)
}
print("regular: \(Date().timeIntervalSince(t1))")
//Swift
let t2 = Date()
var mat2: [[Double]] = stride(from: 0, to: stream.count, by: cols).map {
let end = stream.endIndex
let chunkEnd = stream.index($0, offsetBy: cols, limitedBy: end) ?? end
return Array(stream[$0..<chunkEnd])
}
print("swift: \(Date().timeIntervalSince(t2))")
and out:
regular: 0.0449600219726562
swift: 0.49255496263504
public extension Optional {
/// Wraps a value in an `Optional`, based on a condition.
/// - Parameters:
/// - wrapped: A non-optional value.
/// - getIsNil: The condition that will result in `nil`.
init(
_ wrapped: Wrapped,
nilWhen getIsNil: (Wrapped) throws -> Bool
) rethrows {
self = try getIsNil(wrapped) ? nil : wrapped
}
}
public extension Sequence {
/// Splits a `Sequence` into equal "chunks".
///
/// - Parameter maxArrayCount: The maximum number of elements in a chunk.
/// - Returns: `Array`s with `maxArrayCount` `counts`,
/// until the last chunk, which may be smaller.
subscript(maxArrayCount maxCount: Int) -> AnySequence<[Element]> {
.init(
sequence( state: makeIterator() ) { iterator in
Optional(
(0..<maxCount).compactMap { _ in iterator.next() },
nilWhen: \.isEmpty
)
}
)
}
}
// [ ["1", "2"], ["3", "4"], ["5", "6"], ["7"] ]"
(1...7).map(String.init)[maxArrayCount: 2]
public extension Collection {
/// Splits a `Collection` into equal "chunks".
///
/// - Parameter maxSubSequenceCount: The maximum number of elements in a chunk.
/// - Returns: `SubSequence`s with `maxSubSequenceLength` `counts`,
/// until the last chunk, which may be smaller.
subscript(maxSubSequenceCount maxCount: Int) -> AnySequence<SubSequence> {
.init(
sequence(state: startIndex) { startIndex in
guard startIndex < self.endIndex
else { return nil }
let endIndex =
self.index(startIndex, offsetBy: maxCount, limitedBy: self.endIndex)
?? self.endIndex
defer { startIndex = endIndex }
return self[startIndex..<endIndex]
}
)
}
}
// ["12", "34", "56", "7"]
(1...7).map(String.init).joined()[maxSubSequenceCount: 2]

Split Sequence/Collection Into Chunks [duplicate]

Starting with a large [String] and a given subarray size, what is the best way I could go about splitting up this array into smaller arrays? (The last array will be smaller than the given subarray size).
Concrete example:
Split up ["1","2","3","4","5","6","7"] with max split size 2
The code would produce [["1","2"],["3","4"],["5","6"],["7"]]
Obviously I could do this a little more manually, but I feel like in swift something like map() or reduce() may do what I want really beautifully.
In Swift 3/4 this would look like the following:
let numbers = ["1","2","3","4","5","6","7"]
let chunkSize = 2
let chunks = stride(from: 0, to: numbers.count, by: chunkSize).map {
Array(numbers[$0..<min($0 + chunkSize, numbers.count)])
}
// prints as [["1", "2"], ["3", "4"], ["5", "6"], ["7"]]
As an extension to Array:
extension Array {
func chunked(by chunkSize: Int) -> [[Element]] {
return stride(from: 0, to: self.count, by: chunkSize).map {
Array(self[$0..<Swift.min($0 + chunkSize, self.count)])
}
}
}
Or the slightly more verbose, yet more general:
let numbers = ["1","2","3","4","5","6","7"]
let chunkSize = 2
let chunks: [[String]] = stride(from: 0, to: numbers.count, by: chunkSize).map {
let end = numbers.endIndex
let chunkEnd = numbers.index($0, offsetBy: chunkSize, limitedBy: end) ?? end
return Array(numbers[$0..<chunkEnd])
}
This is more general because I am making fewer assumptions about the type of the index into the collection. In the previous implementation I assumed that they could be could be compared and added.
Note that in Swift 3 the functionality of advancing indices has been transferred from the indices themselves to the collection.
With Swift 5, according to your needs, you can choose one of the five following ways in order to solve your problem.
1. Using AnyIterator in a Collection extension method
AnyIterator is a good candidate to iterate over the indices of an object that conforms to Collection protocol in order to return subsequences of this object. In a Collection protocol extension, you can declare a chunked(by:) method with the following implementation:
extension Collection {
func chunked(by distance: Int) -> [[Element]] {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
var index = startIndex
let iterator: AnyIterator<Array<Element>> = AnyIterator({
let newIndex = self.index(index, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex
defer { index = newIndex }
let range = index ..< newIndex
return index != self.endIndex ? Array(self[range]) : nil
})
return Array(iterator)
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
2. Using stride(from:to:by:) function in an Array extension method
Array indices are of type Int and conform to Strideable protocol. Therefore, you can use stride(from:to:by:) and advanced(by:) with them. In an Array extension, you can declare a chunked(by:) method with the following implementation:
extension Array {
func chunked(by distance: Int) -> [[Element]] {
let indicesSequence = stride(from: startIndex, to: endIndex, by: distance)
let array: [[Element]] = indicesSequence.map {
let newIndex = $0.advanced(by: distance) > endIndex ? endIndex : $0.advanced(by: distance)
//let newIndex = self.index($0, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex // also works
return Array(self[$0 ..< newIndex])
}
return array
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
3. Using a recursive approach in an Array extension method
Based on Nate Cook recursive code, you can declare a chunked(by:) method in an Array extension with the following implementation:
extension Array {
func chunked(by distance: Int) -> [[Element]] {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
if self.count <= distance {
return [self]
} else {
let head = [Array(self[0 ..< distance])]
let tail = Array(self[distance ..< self.count])
return head + tail.chunked(by: distance)
}
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
4. Using a for loop and batches in a Collection extension method
Chris Eidhof and Florian Kugler show in Swift Talk #33 - Sequence & Iterator (Collections #2) video how to use a simple for loop to fill batches of sequence elements and append them on completion to an array. In a Sequence extension, you can declare a chunked(by:) method with the following implementation:
extension Collection {
func chunked(by distance: Int) -> [[Element]] {
var result: [[Element]] = []
var batch: [Element] = []
for element in self {
batch.append(element)
if batch.count == distance {
result.append(batch)
batch = []
}
}
if !batch.isEmpty {
result.append(batch)
}
return result
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
5. Using a custom struct that conforms to Sequence and IteratorProtocol protocols
If you don't want to create extensions of Sequence, Collection or Array, you can create a custom struct that conforms to Sequence and IteratorProtocol protocols. This struct should have the following implementation:
struct BatchSequence<T>: Sequence, IteratorProtocol {
private let array: [T]
private let distance: Int
private var index = 0
init(array: [T], distance: Int) {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
self.array = array
self.distance = distance
}
mutating func next() -> [T]? {
guard index < array.endIndex else { return nil }
let newIndex = index.advanced(by: distance) > array.endIndex ? array.endIndex : index.advanced(by: distance)
defer { index = newIndex }
return Array(array[index ..< newIndex])
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let batchSequence = BatchSequence(array: array, distance: 2)
let newArray = Array(batchSequence)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
I wouldn't call it beautiful, but here's a method using map:
let numbers = ["1","2","3","4","5","6","7"]
let splitSize = 2
let chunks = numbers.startIndex.stride(to: numbers.count, by: splitSize).map {
numbers[$0 ..< $0.advancedBy(splitSize, limit: numbers.endIndex)]
}
The stride(to:by:) method gives you the indices for the first element of each chunk, so you can map those indices to a slice of the source array using advancedBy(distance:limit:).
A more "functional" approach would simply be to recurse over the array, like so:
func chunkArray<T>(s: [T], splitSize: Int) -> [[T]] {
if countElements(s) <= splitSize {
return [s]
} else {
return [Array<T>(s[0..<splitSize])] + chunkArray(Array<T>(s[splitSize..<s.count]), splitSize)
}
}
I like Nate Cook's answer, it looks like Swift has moved on since it was written, here's my take on this as an extension to Array:
extension Array {
func chunk(chunkSize : Int) -> Array<Array<Element>> {
return 0.stride(to: self.count, by: chunkSize)
.map { Array(self[$0..<$0.advancedBy(chunkSize, limit: self.count)]) }
}
}
Note, it returns [] for negative numbers and will result in a fatal error as written above. You'll have to put a guard in if you want to prevent that.
func testChunkByTwo() {
let input = [1,2,3,4,5,6,7]
let output = input.chunk(2)
let expectedOutput = [[1,2], [3,4], [5,6], [7]]
XCTAssertEqual(expectedOutput, output)
}
func testByOne() {
let input = [1,2,3,4,5,6,7]
let output = input.chunk(1)
let expectedOutput = [[1],[2],[3],[4],[5],[6],[7]]
XCTAssertEqual(expectedOutput, output)
}
func testNegative() {
let input = [1,2,3,4,5,6,7]
let output = input.chunk(-2)
let expectedOutput = []
XCTAssertEqual(expectedOutput, output)
}
I don't think you'll want to use map or reduce. Map is for applying a function on each individual element in an array while reduce is for flattening an array. What you want to do is slice the array into subarrays of a certain size. This snippet uses slices.
var arr = ["1","2","3","4","5","6","7"]
var splitSize = 2
var newArr = [[String]]()
var i = 0
while i < arr.count {
var slice: Slice<String>!
if i + splitSize >= arr.count {
slice = arr[i..<arr.count]
}
else {
slice = arr[i..<i+splitSize]
}
newArr.append(Array(slice))
i += slice.count
}
println(newArr)
Would be nice to express Tyler Cloutier's formulation as an extension on Array:
extension Array {
func chunked(by chunkSize:Int) -> [[Element]] {
let groups = stride(from: 0, to: self.count, by: chunkSize).map {
Array(self[$0..<[$0 + chunkSize, self.count].min()!])
}
return groups
}
}
This gives us a general way to partition an array into chunks.
New in Swift 4, you can do this efficiently with reduce(into:). Here's an extension on Sequence:
extension Sequence {
func eachSlice(_ clump:Int) -> [[Self.Element]] {
return self.reduce(into:[]) { memo, cur in
if memo.count == 0 {
return memo.append([cur])
}
if memo.last!.count < clump {
memo.append(memo.removeLast() + [cur])
} else {
memo.append([cur])
}
}
}
}
Usage:
let result = [1,2,3,4,5,6,7,8,9].eachSlice(2)
// [[1, 2], [3, 4], [5, 6], [7, 8], [9]]
The above is very cleaver, but it makes my head hurt. I had to revert back to a less swifty approach.
For Swift 2.0
var chunks = [[Int]]()
var temp = [Int]()
var splitSize = 3
var x = [1,2,3,4,5,6,7]
for (i, element) in x.enumerate() {
if temp.count < splitSize {
temp.append(element)
}
if temp.count == splitSize {
chunks.append(temp)
temp.removeAll()
}
}
if !temp.isEmpty {
chunks.append(temp)
}
Playground Result [[1, 2, 3], [4, 5, 6], [7]]
I'll just throw my hat in the ring here with another implementation based on AnyGenerator.
extension Array {
func chunks(_ size: Int) -> AnyIterator<[Element]> {
if size == 0 {
return AnyIterator {
return nil
}
}
let indices = stride(from: startIndex, to: count, by: size)
var generator = indices.makeIterator()
return AnyIterator {
guard let i = generator.next() else {
return nil
}
var j = self.index(i, offsetBy: size)
repeat {
j = self.index(before: j)
} while j >= self.endIndex
return self[i...j].lazy.map { $0 }
}
}
}
I prefer this method since it relies exclusively on generators which can have a non-negligible, positive memory impact when dealing with large arrays.
For your specific example, here's how it would work:
let chunks = Array(["1","2","3","4","5","6","7"].chunks(2))
Result:
[["1", "2"], ["3", "4"], ["5", "6"], ["7"]]
In Swift 4 or later you can also extend Collection and return a collection of SubSequence of it to be able to use it also with StringProtocol types (String or Substring). This way it will return a collection of substrings instead of a collection of a bunch of characters:
Xcode 10.1 • Swift 4.2.1 or later
extension Collection {
func subSequences(limitedTo maxLength: Int) -> [SubSequence] {
precondition(maxLength > 0, "groups must be greater than zero")
var start = startIndex
var subSequences: [SubSequence] = []
while start < endIndex {
let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
defer { start = end }
subSequences.append(self[start..<end])
}
return subSequences
}
}
Or as suggested in comments by #Jessy using collection method
public func sequence<T, State>(state: State, next: #escaping (inout State) -> T?) -> UnfoldSequence<T, State>
extension Collection {
func subSequences(limitedTo maxLength: Int) -> [SubSequence] {
precondition(maxLength > 0, "groups must be greater than zero")
return .init(sequence(state: startIndex) { start in
guard start < self.endIndex else { return nil }
let end = self.index(start, offsetBy: maxLength, limitedBy: self.endIndex) ?? self.endIndex
defer { start = end }
return self[start..<end]
})
}
}
Usage
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let slices = array.subSequences(limitedTo: 2) // [ArraySlice(["1", "2"]), ArraySlice(["3", "4"]), ArraySlice(["5", "6"]), ArraySlice(["7", "8"]), ArraySlice(["9"])]
for slice in slices {
print(slice) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
}
// To convert from ArraySlice<Element> to Array<element>
let arrays = slices.map(Array.init) // [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
extension Collection {
var singles: [SubSequence] { return subSequences(limitedTo: 1) }
var pairs: [SubSequence] { return subSequences(limitedTo: 2) }
var triples: [SubSequence] { return subSequences(limitedTo: 3) }
var quads: [SubSequence] { return subSequences(limitedTo: 4) }
}
Array or ArraySlice of Characters
let chars = ["a","b","c","d","e","f","g","h","i"]
chars.singles // [["a"], ["b"], ["c"], ["d"], ["e"], ["f"], ["g"], ["h"], ["i"]]
chars.pairs // [["a", "b"], ["c", "d"], ["e", "f"], ["g", "h"], ["i"]]
chars.triples // [["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]]
chars.quads // [["a", "b", "c", "d"], ["e", "f", "g", "h"], ["i"]]
chars.dropFirst(2).quads // [["c", "d", "e", "f"], ["g", "h", "i"]]
StringProtocol Elements (String and SubString)
let str = "abcdefghi"
str.singles // ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
str.pairs // ["ab", "cd", "ef", "gh", "i"]
str.triples // ["abc", "def", "ghi"]
str.quads // ["abcd", "efgh", "i"]
str.dropFirst(2).quads // ["cdef", "ghi"]
Swift 5.1 - General solution for all kind of Collections:
extension Collection where Index == Int {
func chunked(by chunkSize: Int) -> [[Element]] {
stride(from: startIndex, to: endIndex, by: chunkSize).map { Array(self[$0..<Swift.min($0 + chunkSize, count)]) }
}
}
Do you know that any solution with [a...b] swift style works 10 times slower then regular?
for y in 0..<rows {
var row = [Double]()
for x in 0..<cols {
row.append(stream[y * cols + x])
}
mat.append(row)
}
Try it and will see, here is my raw code for test:
let count = 1000000
let cols = 1000
let rows = count / cols
var stream = [Double].init(repeating: 0.5, count: count)
// Regular
var mat = [[Double]]()
let t1 = Date()
for y in 0..<rows {
var row = [Double]()
for x in 0..<cols {
row.append(stream[y * cols + x])
}
mat.append(row)
}
print("regular: \(Date().timeIntervalSince(t1))")
//Swift
let t2 = Date()
var mat2: [[Double]] = stride(from: 0, to: stream.count, by: cols).map {
let end = stream.endIndex
let chunkEnd = stream.index($0, offsetBy: cols, limitedBy: end) ?? end
return Array(stream[$0..<chunkEnd])
}
print("swift: \(Date().timeIntervalSince(t2))")
and out:
regular: 0.0449600219726562
swift: 0.49255496263504
public extension Optional {
/// Wraps a value in an `Optional`, based on a condition.
/// - Parameters:
/// - wrapped: A non-optional value.
/// - getIsNil: The condition that will result in `nil`.
init(
_ wrapped: Wrapped,
nilWhen getIsNil: (Wrapped) throws -> Bool
) rethrows {
self = try getIsNil(wrapped) ? nil : wrapped
}
}
public extension Sequence {
/// Splits a `Sequence` into equal "chunks".
///
/// - Parameter maxArrayCount: The maximum number of elements in a chunk.
/// - Returns: `Array`s with `maxArrayCount` `counts`,
/// until the last chunk, which may be smaller.
subscript(maxArrayCount maxCount: Int) -> AnySequence<[Element]> {
.init(
sequence( state: makeIterator() ) { iterator in
Optional(
(0..<maxCount).compactMap { _ in iterator.next() },
nilWhen: \.isEmpty
)
}
)
}
}
// [ ["1", "2"], ["3", "4"], ["5", "6"], ["7"] ]"
(1...7).map(String.init)[maxArrayCount: 2]
public extension Collection {
/// Splits a `Collection` into equal "chunks".
///
/// - Parameter maxSubSequenceCount: The maximum number of elements in a chunk.
/// - Returns: `SubSequence`s with `maxSubSequenceLength` `counts`,
/// until the last chunk, which may be smaller.
subscript(maxSubSequenceCount maxCount: Int) -> AnySequence<SubSequence> {
.init(
sequence(state: startIndex) { startIndex in
guard startIndex < self.endIndex
else { return nil }
let endIndex =
self.index(startIndex, offsetBy: maxCount, limitedBy: self.endIndex)
?? self.endIndex
defer { startIndex = endIndex }
return self[startIndex..<endIndex]
}
)
}
}
// ["12", "34", "56", "7"]
(1...7).map(String.init).joined()[maxSubSequenceCount: 2]

Reverse array by group size

I was trying to solve this challenge: reverse an array of elements by groups given a group size.
Given Array: [1, 2, 3, 4, 5, 6]
Desired Result (group size of 3): [4, 5, 6, 1, 2, 3]
If last group has less elements than the group size, then just add them and finish, as follows:
Given Array: [1, 2, 3, 4, 5, 6, 7]
Desired Result: [5, 6, 7, 2, 3, 4, 1]
I tried this and it is working, but it looks kinda weird for me. Can anyone help me find a cleaner or much more intuitive solution?
extension Array {
func reverse(groupSize: Int) -> [Element] {
var reversed = [Element]()
let groups = count / groupSize
for group in 0...groups {
let lowerBound = count - group * groupSize - groupSize
let upperBound = count - 1 - group * groupSize
if lowerBound >= 0 {
reversed += Array(self[lowerBound...upperBound])
} else {
reversed += Array(self[0...upperBound])
}
}
return reversed
}
}
The following solution is based on a combo of stride+map:
let groupSize = 3
let array = [1, 2, 3, 4, 5, 6]
let reversedArray = Array(array.reversed())
let result = stride(from: 0, to: reversedArray.count, by: groupSize).map {
reversedArray[$0 ..< min($0 + groupSize, reversedArray.count)].reversed()
}.reduce([Int](), +)
print(result) // [4, 5, 6, 1, 2, 3]
You could say:
extension Array {
func groupedReversing(stride: Int) -> [Element] {
precondition(stride > 0, "stride must be > 0")
return Swift.stride(from: count, to: 0, by: -stride)
.flatMap { self[Swift.max(0, $0 - stride) ..< $0] }
}
}
let result = Array(1 ... 7).groupedReversing(stride: 3)
print(result) // [5, 6, 7, 2, 3, 4, 1]
We're using stride(from:through:by:) to iterate from array.count (inclusive) to 0 (exclusive) in increments of (minus) the stride. The Swift. prefix is in order to disambiguate it from the obsoleted Swift 2 stride method (which will be gone in Swift 4.1).
Then, we're flat-mapping the index to a slice of the input array that's up to stride elements long (truncating at the beginning of the array as we clamp the lower index to 0). And because this is flatMap, the resulting slices are concatenated into a single resulting array.
You could also implement a fully generic version across Sequence by first providing an implementation on BidirectionalCollection, advancing indices backwards and appending slices into a resulting array:
extension BidirectionalCollection {
func groupedReversing(stride: Int) -> [Element] {
precondition(stride > 0, "stride must be > 0")
var result: [Element] = []
result.reserveCapacity(numericCast(count))
var upper = endIndex
while upper != startIndex {
// get the next lower bound for the slice, stopping at the start index.
let lower = index(upper, offsetBy: -numericCast(stride),
limitedBy: startIndex) ?? startIndex
result += self[lower ..< upper]
upper = lower
}
return result
}
}
and then implement an overload on Sequence that first converts to an array, and then forwards onto the above implementation:
extension Sequence {
func groupedReversing(stride: Int) -> [Element] {
return Array(self).groupedReversing(stride: stride)
}
}
Now you can call it on, for example, a CountableClosedRange without first having to convert it to an array:
let result = (1 ... 7).groupedReversing(stride: 3)
print(result) // [5, 6, 7, 2, 3, 4, 1]
I think your function is okay, not sure what you meant by weird tbh, can separate to chunk or add each elements in reversed way ,but logic is same anyway, just need to mind the performance/complexity of each ways:
let a = [1,2,3,4,5,6,7,8,9,10,11]
extension Array {
func reverse(group: Int) -> [Element] {
guard group > 1 else { return self.reversed() }
var new = [Element]()
for i in stride(from: self.count-1, through: 0, by: -group) {
let k = i-group+1 < 0 ? 0 : i-group+1
for j in k...i {
new.append(self[j])
}
}
return new
}
}
a.reverse(group: 4) //[8, 9, 10, 11, 4, 5, 6, 7, 1, 2, 3]
The two following Swift 5 code snippets show how to implement a Collection or Array extension method in order to chunked it, reverse it then flatten it into a new array.
#1. Using AnyIterator and Sequence joined()
extension Collection {
func reverseFlattenChunked(by distance: Int) -> [Element] {
precondition(distance > 0, "distance must be greater than 0")
var index = endIndex
let iterator = AnyIterator({ () -> SubSequence? in
let newIndex = self.index(index, offsetBy: -distance, limitedBy: self.startIndex) ?? self.startIndex
defer { index = newIndex }
return index != self.startIndex ? self[newIndex ..< index] : nil
})
return Array(iterator.joined())
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7"]
let newArray = array.reverseFlattenChunked(by: 3)
print(newArray) // prints: ["5", "6", "7", "2", "3", "4", "1"]
let array: [String] = ["1", "2", "3", "4", "5", "6"]
let newArray = array.reverseFlattenChunked(by: 2)
print(newArray) // prints: ["5", "6", "3", "4", "1", "2"]
let array: [String] = []
let newArray = array.reverseFlattenChunked(by: 3)
print(newArray) // prints: []
#2. Using stride(from:to:by:) and Sequence flatMap(_:)
extension Array {
func reverseFlattenChunked(by distance: Int) -> [Element] {
precondition(distance > 0, "distance must be greater than 0")
let indicesSequence = stride(from: self.endIndex, to: self.startIndex, by: -distance)
let array = indicesSequence.flatMap({ (index) -> SubSequence in
let advancedIndex = self.index(index, offsetBy: -distance, limitedBy: self.startIndex) ?? self.startIndex
// let advancedIndex = index.advanced(by: -distance) <= self.startIndex ? self.startIndex : index.advanced(by: -distance) // also works
return self[advancedIndex ..< index]
})
return array
}
}
Usage:
let array = ["1", "2", "3", "4", "5", "6", "7"]
let newArray = array.reverseFlattenChunked(by: 3)
print(newArray) // prints: ["5", "6", "7", "2", "3", "4", "1"]
let array: [String] = ["1", "2", "3", "4", "5", "6"]
let newArray = array.reverseFlattenChunked(by: 2)
print(newArray) // prints: ["5", "6", "3", "4", "1", "2"]
let array: [String] = []
let newArray = array.reverseFlattenChunked(by: 3)
print(newArray) // prints: []

Most common dictionary in array of dictionaries

I need to find the most common dictionary in an array of swift dictionaries. I tried using the following :
func frequencies
<S: SequenceType where S.Generator.Element: Hashable>
(source: S) -> [(S.Generator.Element,Int)] {
var frequency: [S.Generator.Element:Int] = [:]
for x in source {
frequency[x] = (frequency[x] ?? 0) + 1
}
return sorted(frequency) { $0.1 > $1.1 }
}
But I can't invoke 'frequencies' with an argument list of type [[String:String]](). How can I edit the above function to take an array of dictionaries, or use another method entirely?
As the comments have mentioned, the problem is that the type [String:String] is not Hashable.
A (much) less efficient solution when your types are not hashable is to fall back to Comparable (could sort and generate running totals) or Equatable or, worst case, require the caller to supply a isEquivalent closure. Then you go hunting through your running frequencies searching for an equivalent item (and if you don't find one, insert it with a frequency of 1).
Here is an implementation that does that in Swift 2.0:
extension SequenceType {
func frequencies(#noescape isEquivalent: (Generator.Element,Generator.Element) -> Bool) -> [(Generator.Element,Int)] {
var frequency: [(Generator.Element,Int)] = []
for x in self {
// find the index of the equivalent entry
if let idx = frequency.indexOf({ isEquivalent($0.0, x)}) {
// and bump the frequency
frequency[idx].1 += 1
}
else {
// add a new entry
frequency.append(x,1)
}
}
return frequency.sort { $0.1 > $1.1 }
}
}
Since there is an implementation of == that compares two dictionaries, so long as those dictionaries contain values that are equatable, you can call it like this:
let dicts = [
["name": "David", "number": "1"],
["name": "John", "number": "2"],
["name": "David", "number": "1"],
]
// you can use `==` in two dictionaries that contain an equatable value,
// such as String here:
dicts[0] == dicts[1] // false
dicts[0] == dicts[2] // true
// so you can call frequencies like so:
dicts.frequencies(==)
which returns:
[(["number": "1", "name": "David"], 2),
(["number": "2", "name": "John"], 1)]
edit: here is a Swift 1.2 version, unfortunately complicated by the absence of a version of find in 1.2 (renamed indexOf in 2.0) that takes a predicate. This ought to work, but I don't have a working copy of 1.2 in this machine so you may need to fix any syntax errors:
extension Array {
// add missing indexOf to Array as 1.2 doesn't have an equivalent
func indexOf(#noescape predicate: T->Bool) -> Int? {
for idx in indices(self) {
if predicate(self[idx]) { return idx }
}
return nil
}
}
func frequencies<S: SequenceType>
(source: S, #noescape isEquivalent: (S.Generator.Element,S.Generator.Element) -> Bool) -> [(S.Generator.Element,Int)] {
var frequency: [(S.Generator.Element,Int)] = []
for x in source {
// find the index of the equivalent entry
if let idx = frequency.indexOf({ isEquivalent($0.0, x)}) {
// and bump the frequency
frequency[idx].1 += 1
}
else {
// add a new entry
frequency.append(x,1)
}
}
return sorted(frequency) { $0.1 > $1.1 }
}
frequencies(dicts, ==)
You could use a NSCountedSet which provides this kind of functionality:
let arr = [
[
"foo": "bar"
],
[
"foo": "bar"
],
[
"foo": "baz"
],
]
let countedSet = NSCountedSet(array: arr)
for dict in countedSet {
println(countedSet.countForObject(dict))
println(dict)
}
prints
2
["foo": "bar"]
1
["foo": "baz"]
If you're having problems with the initializer not woking properly, use:
let countedSet = NSCountedSet()
countedSet.addObjectsFromArray(arr)
instead of
let countedSet = NSCountedSet(array: arr)
I haven't tested with Swift 2, but the usage should be more or less the same.
(I don't know if this works with Swift 1.2)
You can also use a global function:
func frequencies<K, V where K : Hashable, V: Equatable>(dicts: [[K : V]]) -> [(dict: [K : V], count: Int)] {
var counts = [(dict: [K : V], count: Int)]()
for dict in dicts {
if let index = counts.indexOf({ $0.dict == dict }) {
counts[index].1++
} else {
counts.append(dict: dict, count: 1)
}
}
return counts.sort { $0.1 > $1.1 }
}
Use like this
let dicts = [
["name": "David", "number": "1"],
["name": "John" , "number": "2"],
["name": "David", "number": "1"],
]
print(frequencies(dicts))
outputs
[(["number": "1", "name": "David"], 2),
(["number": "2", "name": "John" ], 1)]

Resources