Map or reduce with index in Swift
You can use enumerate
to convert a sequence (Array
, String
, etc.) to a sequence of tuples with an integer counter and and element paired together. That is:
let numbers = [7, 8, 9, 10]
let indexAndNum: [String] = numbers.enumerate().map { (index, element) in
return "\(index): \(element)"
}
print(indexAndNum)
// ["0: 7", "1: 8", "2: 9", "3: 10"]
Link to enumerate
definition
Note that this isn't the same as getting the index of the collection—enumerate
gives you back an integer counter. This is the same as the index for an array, but on a string or dictionary won't be very useful. To get the actual index along with each element, you can use zip
:
let actualIndexAndNum: [String] = zip(numbers.indices, numbers).map { "\($0): \($1)" }
print(actualIndexAndNum)
// ["0: 7", "1: 8", "2: 9", "3: 10"]
When using an enumerated sequence with reduce
, you won't be able to separate the index and element in a tuple, since you already have the accumulating/current tuple in the method signature. Instead, you'll need to use .0
and .1
on the second parameter to your reduce
closure:
let summedProducts = numbers.enumerate().reduce(0) { (accumulate, current) in
return accumulate + current.0 * current.1
// ^ ^
// index element
}
print(summedProducts) // 56
Swift 3.0 and above
Since Swift 3.0 syntax is quite different.
Also, you can use short-syntax/inline to map array on dictionary:
let numbers = [7, 8, 9, 10]
let array: [(Int, Int)] = numbers.enumerated().map { ($0, $1) }
// ^ ^
// index element
That produces:
[(0, 7), (1, 8), (2, 9), (3, 10)]
Map/reduce with index in Swift 4
This is equivalent
let result = (0...2).map({ index in numbers.reduce(0) { (sum, current) in sum + current + index } })
or this if you want to use result
array
let r = result.enumerated().map({ (index, _) in numbers.reduce(0) { (sum, current) in sum + current + index } })
but more efficient will be something like that
let sum = numbers.reduce(0, +)
let resultsExpected = 3
let result = (0..<resultsExpected).map({ $0 * numbers.count + sum })
Swift: Array map or reduce, enumerate with index
// must be sorted by 'day'!!!
let arrA0 = [2.45, 2.75, 2.9, 3.1, 3.2, 3.3]
// associated values
let arrA1 = [145.0, 150.0, 160.0, 245.0, 250.0, 260.0]
let arr = Array(zip(arrA0, arrA1))
// now i have an array of tuples, where tuple.0 is key and tuple.1 etc. is associated value
// you can expand the tuple for as much associated values, as you want
print(arr)
// [(2.45, 145.0), (2.75, 150.0), (2.9, 160.0), (3.1, 245.0), (3.2, 250.0), (3.3, 260.0)]
// now i can perform my 'calculations' the most effective way
var res:[Int:(Double,Double)] = [:]
// sorted set of Int 'day' values
let set = Set(arr.map {Int($0.0)}).sort()
// for two int values the sort is redundant, but
// don't be depend on that!
print(set)
// [2, 3]
var g = 0
var j = 0
set.forEach { (i) -> () in
var sum1 = 0.0
var sum2 = 0.0
var t = true
while t && g < arr.count {
let v1 = arr[g].0
let v2 = arr[g].1
t = i == Int(v1)
if t {
g++
j++
} else {
break
}
sum1 += v1
sum2 += v2
}
res[i] = (sum1 / Double(j), sum2 / Double(j))
j = 0
}
print(res)
// [2: (2.7, 151.666666666667), 3: (3.2, 251.666666666667)]
see, that every element of your data is process only once in the 'calculation', independent from size of 'key' set size
use Swift's Double instead of CGFloat! this increase the speed too :-)
and finally what you are looking for
if let (day, value) = res[2] {
print(day, value) // 2.7 151.666666666667
}
What's the cleanest way of applying map() to a dictionary in Swift?
Swift 4+
Good news! Swift 4 includes a mapValues(_:)
method which constructs a copy of a dictionary with the same keys, but different values. It also includes a filter(_:)
overload which returns a Dictionary
, and init(uniqueKeysWithValues:)
and init(_:uniquingKeysWith:)
initializers to create a Dictionary
from an arbitrary sequence of tuples. That means that, if you want to change both the keys and values, you can say something like:
let newDict = Dictionary(uniqueKeysWithValues:
oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
There are also new APIs for merging dictionaries together, substituting a default value for missing elements, grouping values (converting a collection into a dictionary of arrays, keyed by the result of mapping the collection over some function), and more.
During discussion of the proposal, SE-0165, that introduced these features, I brought up this Stack Overflow answer several times, and I think the sheer number of upvotes helped demonstrate the demand. So thanks for your help making Swift better!
Call Array.reduce(_:_) from enumerated array
You can access the element
of the tuple with val.element
and the index
with val.offset
:
let result = [1,2,3].enumerated().reduce(0, { cur, val in
return val.element
})
Alternatively, you can use assignment to access the values in the tuple:
let result = [1,2,3].enumerated().reduce(0, { cur, val in
let (index, element) = val
return element
})
Swift - Reducing a Dictionary of Arrays to a single array of same type using map/reduce/flatmap
All you need is a single flatMap
:
let result = dict.flatMap { _, values in values }
Swift performance: map() and reduce() vs for loops
Shouldn't the built-in Array methods be faster than the naive approach
for performing such operations? Maybe somebody with more low-level knowledge than I can shed some light on the situation.
I just want to attempt to address this part of the question and more from the conceptual level (with little understanding of the nature of Swift's optimizer on my part) with a "not necessarily". It's coming more from a background in compiler design and computer architecture than deep-rooted knowledge of the nature of Swift's optimizer.
Calling Overhead
With functions like map
and reduce
accepting functions as inputs, it places a greater strain on the optimizer to put it one way. The natural temptation in such a case short of some very aggressive optimization is to constantly branch back and forth between the implementation of, say, map
, and the closure you provided, and likewise transmit data across these disparate branches of code (through registers and stack, typically).
That kind of branching/calling overhead is very difficult for the optimizer to eliminate, especially given the flexibility of Swift's closures (not impossible but conceptually quite difficult). C++ optimizers can inline function object calls but with far more restrictions and code generation techniques required to do it where the compiler would effectively have to generate a whole new set of instructions for map
for each type of function object you pass in (and with explicit aid of the programmer indicating a function template used for the code generation).
So it shouldn't be of great surprise to find that your hand-rolled loops can perform faster -- they put a great deal of less strain on the optimizer. I have seen some people cite that these higher-order functions should be able to go faster as a result of the vendor being able to do things like parallelize the loop, but to effectively parallelize the loop would first require the kind of information that would typically allow the optimizer to inline the nested function calls within to a point where they become as cheap as the hand-rolled loops. Otherwise the function/closure implementation you pass in is going to be effectively opaque to functions like map/reduce
: they can only call it and pay the overhead of doing so, and cannot parallelize it since they cannot assume anything about the nature of the side effects and thread-safety in doing so.
Of course this is all conceptual -- Swift may be able to optimize these cases in the future, or it may already be able to do so now (see -Ofast
as a commonly-cited way to make Swift go faster at the cost of some safety). But it does place a heavier strain on the optimizer, at the very least, to use these kinds of functions over the hand-rolled loops, and the time differences you're seeing in the first benchmark seem to reflect the kind of differences one might expect with this additional calling overhead. Best way to find out is to look at the assembly and try various optimization flags.
Standard Functions
That's not to discourage the use of such functions. They do more concisely express intent, they can boost productivity. And relying on them could allow your codebase to get faster in future versions of Swift without any involvement on your part. But they aren't necessarily always going to be faster -- it is a good general rule to think that a higher-level library function that more directly expresses what you want to do is going to be faster, but there are always exceptions to the rule (but best discovered in hindsight with a profiler in hand since it's far better to err on the side of trust than distrust here).
Artificial Benchmarks
As for your second benchmark, it is almost certainly a result of the compiler optimizing away code that has no side effects that affect user output. Artificial benchmarks have a tendency to be notoriously misleading as a result of what optimizers do to eliminate irrelevant side effects (side effects that don't affect user output, essentially). So you have to be careful there when constructing benchmarks with times that seem too good to be true that they aren't the result of the optimizer merely skipping all the work you actually wanted to benchmark. At the very least, you want your tests to output some final result gathered from the computation.
map inside map error? move the index outside
Based on Joakim Danielson's answer in your previous question you can do it without any loop by zip
ping the arrays
let converModel = zip(array3, array4)
.map{ DataModel(image: $0.0, name: $0.1, dataModel2: zip(array1, array2).map(DataModel2.init)) }
Mapping custom array in Swift
Initializers can be used as functions using this syntax.
["banana", "apple", "mango"].enumerated().map(Fruit.init)
It appears that your Fruit
type matches up with EnumeratedSequence.Element exactly.
struct Fruit {
let id: Int
let name: String
}
If it didn't, you'd just need to create your own closure.
["banana", "apple", "mango"].enumerated().map {
Fruit(id: $0.offset, name: $0.element)
}
Swift Array Extension to replace value of index n by the sum of the n-previous values
Try this:
let myArray = [1, 2, 3, 4, 5]
myArray.reduce([Int](), {accumulator, element in
return accumulator + [(accumulator.last ?? 0) + element]
})
//[1, 3, 6, 10, 15]
What this reduce
does is:
- Start with an empty array
- With each element from
myArray
it calculates its sum with the last element in theaccumulator
- Return the previous array plus the last sum
Here is a simpler, but longer version:
let myArray = [1, 2, 3, 4, 5]
let newArray = myArray.reduce([Int](), {accumulator, element in
var tempo = accumulator
let lastElementFromTheAccumulator = accumulator.last ?? 0
let currentSum = element + lastElementFromTheAccumulator
tempo.append(currentSum)
return tempo
})
print(newArray) //[1, 3, 6, 10, 15]
A more efficient solution, as suggested by Martin R in the comments, uses reduce(into:)
:
myArray.reduce(into: [Int]()) { (accumulator, element) in
accumulator += [(accumulator.last ?? 0) + element]
}
//[1, 3, 6, 10, 15]
And you could have it as an extension:
extension Array where Element: Numeric {
func indexSum() -> [Element] {
return self.reduce([Element](), {acc, element in
return acc + [(acc.last ?? 0) + element]
})
}
}
myArray.indexSum() //[1, 3, 6, 10, 15]
Here a solution that will work with strings too:
extension Array where Element == String {
func indexSum() -> [String] {
return self.reduce(into: [String]()) {(accumulator, element) in
accumulator += [(accumulator.last ?? "") + element]
}
}
}
["a", "b", "c", "d"].indexSum() //["a", "ab", "abc", "abcd"]
If you'd like to have a separator between the elements of the initial array elements, you could use this extension:
extension Array where Element == String {
func indexSum(withSparator: String) -> [String] {
return self.reduce(into: [String]()) {(accumulator, element) in
var previousString = ""
if let last = accumulator.last {
previousString = last + " "
}
accumulator += [previousString + element]
}
}
}
["a", "b", "c", "d"].indexSum(withSparator: " ") //["a", "a b", "a b c", "a b c d"]
Related Topics
Define a Swift Protocol Which Requires a Specific Type of Sequence
Swift Extension on Generic Struct Based on Properties of Type T
Suppressing Implicit Returns in Swift
Nsdatecomponents Weekofyear Returns Wrong Date
How to Implement Protocol Methods That Return Covariant Selfs
Controlling Size of Texteditor in Swiftui
Swift: Programmatically Enumerate Outgoing Segues from a Uiviewcontroller
Realitykit as a Framework to Build 3D Nonar Apps
Wkwebview Auto Fill Login Form Swift 2
Nsdata Contentsofurl Constructor Returns Nil
Accessor Gives the Wrong Value in Swift 1.2/2.0 Release Build Only
Collectionview Not Display Data After Parsing JSON
How to Encode an Unmanaged<Seckey> to Base64 to Send to Another Server
Executing Text-To-Speech in Order
Getting a Let Value Outside a Function
How to Compare Just the Time of a Date in Swift