If I have two int arrays such as
var array1 = [1,2,3]
var array2 = [1,2,3,5]
I'd like to be able to add the first element of the first array with the first element of the second array, and so on. However if an array has a different length than the other I'd like to keep the element that was not added in the return array. For this example my return array would be [2,4,6,5].
I tried using zip(array1,array2).map(+) but it would exclude the 5 from array2.
After adding the elements at the index positions which are common to both arrays (what you already did with zip and map) just append the remaining elements from both arrays (using append(contentsOf:) and dropFirst):
let array1 = [1, 2, 3]
let array2 = [1, 2, 3, 5]
var combined = zip(array1, array2).map(+)
let commonCount = combined.count
combined.append(contentsOf: array1.dropFirst(commonCount))
combined.append(contentsOf: array2.dropFirst(commonCount))
print(combined) // [2, 4, 6, 5]
AnySequence(zip: (1...3, [1, 2, 3, 5])).map {
Optional($0).map(+) ?? firstNonNil($0)!
}
public extension AnySequence {
/// Like `zip`, but with `nil` elements for the shorter sequence after it is exhausted.
init<Sequence0: Sequence, Sequence1: Sequence>(
zip zipped: (Sequence0, Sequence1)
) where Element == (Sequence0.Element?, Sequence1.Element?) {
self.init(
sequence(
state: (zipped.0.makeIterator(), zipped.1.makeIterator())
) { iterators in
((iterators.0.next(), iterators.1.next()) as Optional)
.filter { $0 != nil || $1 != nil }
}
)
}
}
public extension Optional {
/// Exchange two optionals for a single optional tuple.
/// - Returns: `nil` if either tuple element is `nil`.
init<Wrapped0, Wrapped1>(_ optionals: (Wrapped0?, Wrapped1?))
where Wrapped == (Wrapped0, Wrapped1) {
switch optionals {
case let (wrapped0?, wrapped1?):
self = (wrapped0, wrapped1)
default:
self = nil
}
}
/// Transform `.some` into `.none`, if a condition fails.
/// - Parameters:
/// - isSome: The condition that will result in `nil`, when evaluated to `false`.
func filter(_ isSome: (Wrapped) throws -> Bool) rethrows -> Self {
try flatMap { try isSome($0) ? $0 : nil }
}
}
public func firstNonNil<Element>(_ tuple: (Element?, Element?)) -> Element? {
switch tuple {
case (let _0?, _):
return _0
case (nil, let _1?):
return _1
case (nil, nil):
return nil
}
}
func combine2Arrays(array1:[Int], array2:[Int]) -> [Int] {
var finalArray:[Int] = []
let maxSize = max(array1.count, array2.count)
for i in 0..<maxSize {
let valToAdd1 = (array1.count > i ? array1[i] : 0)
let valToAdd2 = (array2.count > i ? array2[i] : 0)
let finalVal = valToAdd1 + valToAdd2
finalArray.append(finalVal)
}
return finalArray
}
print(combine2Arrays(array1: [1,2,3], array2: [1,2,3,5]))
OR
func combine2Arrays(array1:[Int], array2:[Int]) -> [Int] {
var finalArray:[Int] = zip(array1,array2).map(+)
let largerArray = array1.count > array2.count ? array1 : array2
let smallerArray = array1.count > array2.count ? array2 : array1
let min = smallerArray.count
let max = largerArray.count
for i in min..<max {
finalArray.append(largerArray[i])
}
return finalArray
}
print(combine2Arrays(array1: [1,2,3], array2: [1,2,3,5]))
You can fill your smaller array with zeroes, then use zip. inout means that arrays are mutable, or you can make the copy of function parameters inside the function to make them mutable.
private func combineArrays(array1: inout [Int], array2: inout [Int]) -> [Int] {
let maxSize = max(array1.count, array2.count)
if (array1.count > array2.count) {
array2.append(contentsOf: [Int](repeating: 0, count: maxSize - array2.count))
} else if (array2.count > array1.count) {
array1.append(contentsOf: [Int](repeating: 0, count: maxSize - array1.count))
}
return zip(array1, array2).map(+)
}
//javaScript
var array1 = [1,2,3,4,5];
var array2 = [9,7,8,6,5,6,7];
let a= array1.length;
let b = array2.length;
var array3 = [];
let c = a>b?a:b;
for(let i=0; i<c; i++){
if(i < a && i < b){
array3.push(array1[i] + array2[i]);
} else if(i >= a){
array3.push(array2[i])
} else{
array3.push(array1[i])
}
}
console.log(array3)
I am currently working on an algorithm to find all ranges of a target string.
Example:
Input: s = "acfacfacf", target = "acf"
Output: [(0, 3), (3, 6), (6, 9)]
Note: the upperBound is not an index of the subarray.
This is my current solution:
extension String {
func allRanges(of string: String) -> [(Int, Int)] {
var ranges = [(Int, Int)]()
var set: Set<Int> = []
let chars = Array(self)
let target = Array(string)
for index in 0..<chars.count {
if chars[index] == string.first { set.insert(index) }
for i in set {
if index-i < target.count && chars[index] == target[index-i] {
if index-i == target.count-1 {
ranges += [(i, index+1)]
}
} else {
set.remove(i)
}
}
}
return ranges
}
}
This algorithm does well on strings like "acfacfacf" but does poorly on strings like "aaaaaaaaaaaaaaaaa" where the target is "aaaaaaaa" and the expected result is:
[(0, 8), (1, 9), (2, 10), (3, 11), (4, 12), (5, 13), (6, 14), (7, 15), (8, 16), (9, 17)]
Are there any optimizations that can be done here?
Edit: Also. I understand that using tuples here is not very Swifty, but that is not my biggest concern here.
You can iterate your collection indices dropping the last n elements (the size of your collection minus one), check if the subsequence elements is equal to the other collection, if true return the range otherwise return nil:
extension Collection where Element: Equatable {
func indices<C: Collection>(of collection: C) -> [Index] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).filter {
self[$0..<index($0, offsetBy: size)].elementsEqual(collection)
}
}
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).compactMap {
let range = $0..<index($0, offsetBy: size)
return self[range].elementsEqual(collection) ? range : nil
}
}
}
You can also try to optimize it using collection's method starts(with:) to avoid offseting the collection index on every single iteration:
func starts<PossiblePrefix>(with possiblePrefix: PossiblePrefix) -> Bool where PossiblePrefix : Sequence, Self.Element == PossiblePrefix.Element
extension Collection where Element: Equatable {
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).compactMap {
self[$0...].starts(with: collection) ? $0..<index($0, offsetBy: size) : nil
}
}
}
Or zipping the lower and upper indices of the possible ranges:
extension Collection where Element: Equatable {
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let k = collection.count-1
return zip(indices.dropLast(k),indices.dropFirst(k)).compactMap {
self[$0...].starts(with: collection) ? $0 ..< index(after: $1) : nil
}
}
}
Building off of Leo's post. There appears to be some unnecessary work with performing .start(...) and then index(...). This can be further optimized with
extension Collection where Element: Equatable {
func ranges<C: RandomAccessCollection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).compactMap { idx in
var i = idx
var match = false
collection.drop { element in
match = element == self[i]
i = index(after: i)
return match
}
return match ? idx..<i : nil
}
}
}
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?
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?
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]
array.filter{$0 == element}.count
With Swift 5, according to your needs, you may choose one of the 7 following Playground sample codes to count the occurrences of hashable items in an array.
#1. Using Array's reduce(into:_:) and Dictionary's subscript(_:default:) subscript
let array = [4, 23, 97, 97, 97, 23]
let dictionary = array.reduce(into: [:]) { counts, number in
counts[number, default: 0] += 1
}
print(dictionary) // [4: 1, 23: 2, 97: 3]
#2. Using repeatElement(_:count:) function, zip(_:_:) function and Dictionary's 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]
#3. Using a Dictionary's init(grouping:by:) initializer and mapValues(_:) method
let array = [4, 23, 97, 97, 97, 23]
let dictionary = Dictionary(grouping: array, by: { $0 })
let newDictionary = dictionary.mapValues { (value: [Int]) in
return value.count
}
print(newDictionary) // prints: [97: 3, 23: 2, 4: 1]
#4. Using a Dictionary's init(grouping:by:) initializer and map(_:) method
let array = [4, 23, 97, 97, 97, 23]
let dictionary = Dictionary(grouping: array, by: { $0 })
let newArray = dictionary.map { (key: Int, value: [Int]) in
return (key, value.count)
}
print(newArray) // prints: [(4, 1), (23, 2), (97, 3)]
#5. Using a for loop and Dictionary's subscript(_:) subscript
extension Array where Element: Hashable {
func countForElements() -> [Element: Int] {
var counts = [Element: Int]()
for element in self {
counts[element] = (counts[element] ?? 0) + 1
}
return counts
}
}
let array = [4, 23, 97, 97, 97, 23]
print(array.countForElements()) // prints [4: 1, 23: 2, 97: 3]
#6. Using NSCountedSet and NSEnumerator's map(_:) method (requires Foundation)
import Foundation
extension Array where Element: Hashable {
func countForElements() -> [(Element, Int)] {
let countedSet = NSCountedSet(array: self)
let res = countedSet.objectEnumerator().map { (object: Any) -> (Element, Int) in
return (object as! Element, countedSet.count(for: object))
}
return res
}
}
let array = [4, 23, 97, 97, 97, 23]
print(array.countForElements()) // prints [(97, 3), (4, 1), (23, 2)]
#7. Using NSCountedSet and AnyIterator (requires 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)]
Credits:
Swift Idioms
generic on Collection, using Dictionary
I updated oisdk's answer to Swift2.
16/04/14 I updated this code to Swift2.2
16/10/11 updated to Swift3
Hashable:
extension Sequence where Self.Iterator.Element: Hashable {
private typealias Element = Self.Iterator.Element
func freq() -> [Element: Int] {
return reduce([:]) { (accu: [Element: Int], element) in
var accu = accu
accu[element] = accu[element]?.advanced(by: 1) ?? 1
return accu
}
}
}
Equatable:
extension Sequence where Self.Iterator.Element: Equatable {
private typealias Element = Self.Iterator.Element
func freqTuple() -> [(element: Element, count: Int)] {
let empty: [(Element, Int)] = []
return reduce(empty) { (accu: [(Element, Int)], element) in
var accu = accu
for (index, value) in accu.enumerated() {
if value.0 == element {
accu[index].1 += 1
return accu
}
}
return accu + [(element, 1)]
}
}
}
Usage
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)
Use an NSCountedSet. In Objective-C:
NSCountedSet* countedSet = [[NSCountedSet alloc] initWithArray:array];
for (NSString* string in countedSet)
NSLog (#"String %# occurs %zd times", string, [countedSet countForObject:string]);
I assume that you can translate this into Swift yourself.
How about:
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]
It's generic, so it'll work with whatever your element is, as long as it's hashable:
freq([1, 1, 1, 2, 3, 3]) // [2: 1, 3: 2, 1: 3]
freq([true, true, true, false, true]) // [false: 1, true: 4]
And, if you can't make your elements hashable, you could do it with tuples:
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)]
I like to avoid inner loops and use .map as much as possible.
So if we have an array of string, we can do the following to count the occurrences
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
}
}
prints
["tuples": 5, "awesome": 1, "are": 2, "cool": 1, "shades": 1]
Swift 4
let array = ["FOO", "FOO", "BAR", "FOOBAR"]
// Merging keys with closure for conflicts
let mergedKeysAndValues = Dictionary(zip(array, repeatElement(1, count: array.count)), uniquingKeysWith: +)
// mergedKeysAndValues is ["FOO": 2, "BAR": 1, "FOOBAR": 1]
An other approach would be to use the filter method. I find that the most elegant
var numberOfOccurenses = countedItems.filter(
{
if $0 == "FOO" || $0 == "BAR" || $0 == "FOOBAR" {
return true
}else{
return false
}
}).count
You can use this function to count the occurence of the items in array
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)
}
You can implement it like this -
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]
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]
First Step in Counting Sort.
var inputList = [9,8,5,6,4,2,2,1,1]
var countList : [Int] = []
var max = inputList.maxElement()!
// Iniate an array with specific Size and with intial value.
// We made the Size to max+1 to integrate the Zero. We intiated the array with Zeros because it's Counting.
var countArray = [Int](count: Int(max + 1), repeatedValue: 0)
for num in inputList{
countArray[num] += 1
}
print(countArray)
Two Solutions:
Using forEach loop
let array = [10,20,10,40,10,20,30]
var processedElements = [Int]()
array.forEach({
let element = $0
// Check wether element is processed or not
guard processedElements.contains(element) == false else {
return
}
let elementCount = array.filter({ $0 == element}).count
print("Element: \(element): Count \(elementCount)")
// Add Elements to already Processed Elements
processedElements.append(element)
})
Using Recursive Function
let array = [10,20,10,40,10,20,30]
self.printElementsCount(array: array)
func printElementsCount(array: [Int]) {
guard array.count > 0 else {
return
}
let firstElement = array[0]
let filteredArray = array.filter({ $0 != firstElement })
print("Element: \(firstElement): Count \(array.count - filteredArray.count )")
printElementsCount(array: filteredArray)
}
import Foundation
var myArray:[Int] = []
for _ in stride(from: 0, to: 10, by: 1) {
myArray.append(Int.random(in: 1..<6))
}
// Method 1:
var myUniqueElements = Set(myArray)
print("Array: \(myArray)")
print("Unique Elements: \(myUniqueElements)")
for uniqueElement in myUniqueElements {
var quantity = 0
for element in myArray {
if element == uniqueElement {
quantity += 1
}
}
print("Element: \(uniqueElement), Quantity: \(quantity)")
}
// Method 2:
var myDict:[Int:Int] = [:]
for element in myArray {
myDict[element] = (myDict[element] ?? 0) + 1
}
print(myArray)
for keyValue in myDict {
print("Element: \(keyValue.key), Quantity: \(keyValue.value)")
}
The structure which do the count
struct OccureCounter<Item: Hashable> {
var dictionary = [Item: Int]()
mutating func countHere(_ c: [Item]) {
c.forEach { add(item: $0) }
printCounts()
}
mutating func add(item: Item) {
if let value = dictionary[item] {
dictionary[item] = value + 1
} else {
dictionary[item] = 1
}
}
func printCounts() {
print("::: START")
dictionary
.sorted { $0.value > $1.value }
.forEach { print("::: \($0.value) — \($0.key)") }
let all = dictionary.reduce(into: 0) { $0 += $1.value }
print("::: ALL: \(all)")
print("::: END")
}
}
Usage
struct OccureTest {
func test() {
let z: [Character] = ["a", "a", "b", "a", "b", "c", "d", "e", "f"]
var counter = OccureCounter<Character>()
counter.countHere(z)
}
}
It prints:
::: START
::: 3 — a
::: 2 — b
::: 1 — c
::: 1 — f
::: 1 — e
::: 1 — d
::: ALL: 9
::: END
I'd like to write an extension on CollectionType in Swift that will find the x objects after an object in an array. Obviously it needs be protected to work even if there are no objects after the item.
In my head the signatures something like this:
func itemsAfterItem(item: T, limit: Int?) -> [T]
I can't figure out how to implement it though, could someone help?
A possible implementation for arbitrary collections of
Equatable elements (explanations inline). The main
challenge is to get the parameter types and constraints right.
extension CollectionType where Generator.Element: Equatable,
SubSequence.Generator.Element == Generator.Element {
func itemsAfterItem(item: Generator.Element, limit: Index.Distance?) -> [Generator.Element] {
if let idx = indexOf(item) where idx != endIndex {
// Start after the given item:
let from = idx.advancedBy(1)
// Up to min(from + limit, endIndex):
let to = limit.map { from.advancedBy($0, limit: endIndex) } ?? endIndex
// Return slice as an array:
return Array(self[from..<to])
} else {
// Item not found, or only at the last position.
return []
}
}
}
Understanding the
let to = limit.map { from.advancedBy($0, limit: endIndex) } ?? endIndex
part is left as an exercise to the reader :)
Examples:
[1, 2, 3, 4, 5, 6].itemsAfterItem(2, limit: 2) // [3, 4]
["x", "y", "z"].itemsAfterItem("y", limit: 4) // ["z"]
[1, 2, 3].itemsAfterItem(7, limit: 4) // []
[1.1, 2.2, 3.3].itemsAfterItem(1.1, limit: nil) // [2.2, 3.3]
Example for a non-array collection:
"abcdef".characters.itemsAfterItem("b", limit: 2) // ["c", "d"]
Just because I liked the challenge ;)
extension Array where Element : Equatable {
func itemsAfterItem(item: Element, limit: Int? = nil) -> [Element] {
if let from = self.indexOf(item) where from < self.count - 1 {
if let limit = limit where from + limit < self.count {
return Array(self[from+1...from + limit])
}
return Array(self[from+1...self.count-1])
} else {
return []
}
}
}
For the input
let arr = [1, 2, 4, 6, 9]
It results in
arr.itemsAfterItem(2) // [4, 6, 9]
arr.itemsAfterItem(2, limit: 2) // [4, 6]
arr.itemsAfterItem(2, limit: 100) // [4, 6, 9]
arr.itemsAfterItem(9, limit: 2) // []
arr.itemsAfterItem(3, limit: 100) // []
I think you can try this:
func itemsAfterItem(item: T, limit: Int?) -> [T] {
var counter: Int = 0
var isAfter: Bool = false
let array = [T]()
let newLimit = limit != nil ? limit : myArray.count
for tmpItem in myArray {
if tmpItem == T {
isAfter = true
}
if isAfter && counter < limit {
array.append(tmpItem)
counter += 1
}
}
}
This function will put your T item at the start of the array.
I've not test this function
I am looking for iterator to infinitely iterate collection in a loop mode. So that when end index of collection is reached, then iterator should return element at start index.
The following solution seems working, but I hope it can be made in a better way.
public struct LoopIterator<T: Collection>: IteratorProtocol {
private let collection: T
private var startIndexOffset: T.IndexDistance
public init(collection: T) {
self.collection = collection
startIndexOffset = 0
}
public mutating func next() -> T.Iterator.Element? {
guard !collection.isEmpty else {
return nil
}
let index = collection.index(collection.startIndex, offsetBy: startIndexOffset)
startIndexOffset += T.IndexDistance(1)
if startIndexOffset >= collection.count {
startIndexOffset = 0
}
return collection[index]
}
}
extension Array {
func makeLoopIterator() -> LoopIterator<Array> {
return LoopIterator(collection: self)
}
}
// Testing...
// Will print: 1, 2, 3, 1, 2, 3
var it = [1, 2, 3].makeLoopIterator()
for _ in 0..<6 {
print(it.next())
}
Is it a right way to do custom iterator? What can be improved?
Thanks!
In Swift 3 (which you're using), indexes are intended to be advanced by the collection itself. With that, you can simplify this as follows:
public struct LoopIterator<Base: Collection>: IteratorProtocol {
private let collection: Base
private var index: Base.Index
public init(collection: Base) {
self.collection = collection
self.index = collection.startIndex
}
public mutating func next() -> Base.Iterator.Element? {
guard !collection.isEmpty else {
return nil
}
let result = collection[index]
collection.formIndex(after: &index) // (*) See discussion below
if index == collection.endIndex {
index = collection.startIndex
}
return result
}
}
Now we simply move the index forward, and if it now points to the end, reset it to the beginning. No need for count or IndexDistance.
Note that I've used formIndex here, which exists to improve performance in somewhat obscure cases (specifically around AnyIndex) since your Iterator works on any Collection (and therefore any Index). The simpler version would be index = collection.index(after: index), and that may be better in most cases.
For all the gory details on Swift 3 indices, see SE-0065.
With Swift 5, you can use one of the following examples in order to solve your problem.
#1. Using AnyIterator
As an alternative to creating a new type that conforms to IteratorProtocol, you can use AnyIterator. The following code, based on Rob Napier's answer, shows how to use it:
extension Array {
func makeInfiniteLoopIterator() -> AnyIterator<Element> {
var index = self.startIndex
return AnyIterator({
if self.isEmpty {
return nil
}
let result = self[index]
index = self.index(after: index)
if index == self.endIndex {
index = self.startIndex
}
return result
})
}
}
Usage:
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
for val in infiniteLoopIterator.prefix(5) {
print(val)
}
/*
prints:
1
2
3
1
2
*/
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
let array = Array(infiniteLoopIterator.prefix(7))
print(array) // prints: [1, 2, 3, 1, 2, 3, 1]
let infiniteLoopIterator = [1, 2, 3].makeInfiniteLoopIterator()
let val1 = infiniteLoopIterator.next()
let val2 = infiniteLoopIterator.next()
let val3 = infiniteLoopIterator.next()
let val4 = infiniteLoopIterator.next()
print(String(describing: val1)) // prints: Optional(1)
print(String(describing: val2)) // prints: Optional(2)
print(String(describing: val3)) // prints: Optional(3)
print(String(describing: val4)) // prints: Optional(1)
#2. Using AnySequence
A similar approach is to use AnySequence:
extension Array {
func makeInfiniteSequence() -> AnySequence<Element> {
return AnySequence({ () -> AnyIterator<Element> in
var index = self.startIndex
return AnyIterator({
if self.isEmpty {
return nil
}
let result = self[index]
self.formIndex(after: &index) // alternative to: index = self.index(after: index)
if index == self.endIndex {
index = self.startIndex
}
return result
})
})
}
}
Usage:
let infiniteSequence = [1, 2, 3].makeInfiniteSequence()
for val in infiniteSequence.prefix(5) {
print(val)
}
/*
prints:
1
2
3
1
2
*/
let infiniteSequence = [1, 2, 3].makeInfiniteSequence()
let array = Array(infiniteSequence.prefix(7))
print(array) // prints: [1, 2, 3, 1, 2, 3, 1]