If I want to enumerate an array (say for a map()
function where I would need to use the index of an element together with its value), I could use enumerate()
function. E.g.:
import Foundation
let array: [Double] = [1, 2, 3, 4]
let powersArray = array.enumerate().map() {
pow($0.element, Double($0.index))
}
print("array == \(array)")
print("powersArray == \(powersArray)")
// array == [1.0, 2.0, 3.0, 4.0]
// powersArray == [1.0, 2.0, 9.0, 64.0] <- As expected
Now, if I want to use some sub-sequence from the array, I could use a slice
, and it would allow me to use just the same indices as I would use in the original array (which is just what I want in case if I would use subscript
accessor in a for
loop). E.g.:
let range = 1..<(array.count - 1)
let slice = array[range]
var powersSlice = [Double]()
for index in slice.indices {
powersSlice.append(pow(slice[index], Double(index)))
}
print("powersSlice == \(powersSlice)")
// powersSlice == [2.0, 9.0] <- As expected
However, should I try to use enumerate().map()
approach like I did with original array, then I get totally different behaviour. Instead of slice
's range of indices I would get a new, 0-based range:
let powersSliceEnumerate = slice.enumerate().map() {
pow($0.element, Double($0.index))
}
print("powersSliceEnumerate == \(powersSliceEnumerate)")
// powersSliceEnumerate == [1.0, 3.0] <- Not as expected
Question is whether there is a decent way (i.e. without manual adjustments using offsets, or something) to enumerate a slice using its own indices and not the auto-generated 0-based ones?
enumerate()
returns a sequence of (n, elem)
pairs, where the n
s are consecutive Int
s starting at zero. This makes sense because it is
a protocol extension method for SequenceType
, and an arbitrary sequence
does not necessarily have index associated with the elements.
You would get your expected result with
let powersSlice = slice.indices.map { pow(slice[$0], Double($0)) }
or
let powersSlice = zip(slice.indices, slice).map { pow($1, Double($0)) }
The latter approach could be generalized to a protocol extension method for arbitrary collections:
extension CollectionType {
func indexEnumerate() -> AnySequence<(index: Index, element: Generator.Element)> {
return AnySequence(zip(indices, self))
}
}
This returns a sequence of (index, elem)
pairs where index
is an index of the collection and elem
the corresponding element.
AnySequence
is used to "hide" the specific type
Zip2Sequence<RangeGenerator<Self.Index>, Self>
returned from zip()
from the caller.
Example:
let powersSliceEnumerate = slice.indexEnumerate().map() { pow($0.element, Double($0.index)) }
print("powersSliceEnumerate == \(powersSliceEnumerate)")
// powersSliceEnumerate == [2.0, 9.0]
Update for Swift 3:
extension Collection {
func indexEnumerate() -> AnySequence<(Indices.Iterator.Element, Iterator.Element)> {
return AnySequence(zip(indices, self))
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With