How to Multiply Every Value in a Dictionary in Swift - swift

So, currently I have this dictionary:
var data : [Int:Float] = [0:0,1:1,2:1.414,3:2.732,4:2,5:5.236,6:3.469,7:2.693,8:5.828,9:3.201]
I want to create a new dictionary, say "newData." I want "newData" to have the same keys as "data," but I want to multiply every value in "data" by some constant (say "multiple") to get the values in "newData." How can I do this?
Thanks!

var newData = data
for (key, value) in newData
{
newData[key] = value * multiple
}

Given
let data : [Int:Float] = [0:0,1:1,2:1.414,3:2.732,4:2,5:5.236,6:3.469,7:2.693,8:5.828,9:3.201]
let factor: Float = 2
You can use the reduce method
let multiplied = data.reduce([Int:Float]()) { (var result, elm) -> [Int:Float] in
result[elm.0] = elm.1 * factor
return result
}
The result.
[3: 5.464, 2: 2.828, 4: 4.0, 9: 6.402, 5: 10.472, 6: 6.938, 7: 5.386, 0: 0.0, 8: 11.656, 1: 2.0]
Please ignore the order since Dictionaries do not have one.
Why this solution is better then a for loop?
The code I am suggesting here does follow the Functional Programming paradigm. There are several advantages over the classic for loop:
It's thread safe: since only immutable values are used, you don't have to worry about other threads that could change these values while you are using them.
It's faster: under the hood the elements of the results are processed in parallel
It's less error prone because it's more declarative: we are describing how the result should be, not how to build it.

Another solution based on map
let dict1 = ["a":1, "b":2, "c":3]
// Make a copy since we don't want to modify the original
var dict2 = dict1
let multiple = 5
dict2.map { (k,v) in dict2[k] = v*multiple }
I did some simple performance testing with a 10000 and 100000 element array the various solutions proposed perform like this
For Loop: 10000 elements 1.28 ms, 100000 elements 12.28 ms
Map(): 10000 elements 1.24 ms, 100000 elements 12.23 ms
Reduce(): 10000 elements 2.36 ms, 100000 elements 17.18 ms
But you don't have a 10000+ element array. It's just worth noting the difference.

Since Swift 4, Dictionary has a property called mapValues(_:). mapValues(_:) has the following declaration:
Returns a new dictionary containing the keys of this dictionary with the values transformed by the given closure.
func mapValues<T>(_ transform: (Value) throws -> T) rethrows -> Dictionary<Key, T>
The Swift 5 Playground sample code below shows how to use mapValues(_:) in order to create a new dictionary by multiplying every value of an existing dictionary:
let multiple: Float = 2
let data: [Int : Float] = [0 : 0, 1 : 1, 2 : 1.4, 3 : 2.7, 4 : 2, 5 : 5.2]
let newData = data.mapValues { (value: Float) -> Float in
return value * multiple
}
//let newData = data.mapValues { $0 * multiple } // also works
print(newData) // [3: 5.4, 4: 4.0, 2: 2.8, 0: 0.0, 1: 2.0, 5: 10.4]

Related

Question about closures and functions in swift

numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})
Could somebody explain this code? I think numbers is an array here but map is just a function in the swift library right? Like it's a function that already exists and there exists a version of this function that takes an int function that returns an int? Is that it? Well this is a closure I guess and not an int function but can I think of both those as the same as well? Or is a closure and a function different?
You said:
I think numbers is an array ...
Yes, one might infer from the name numbers and from its subsequent usage that it is an array, but you haven't shared its declaration, so technically we cannot be sure. But let us assume for a second that it is an array of integers.
... but map is just a function in the swift library right?
Yes, it is.
Like it's a function that already exists and there exists a version of this function that takes an int function that returns an int?
Technically, not quite. There is not a rendition of map that specifically “takes an int function that returns an int”. It is a “generic” function that just takes a “closure” and returns an array of elements whose type is dictated by the closure return type. In your example, that closure just happens to take an integer and returns an integer (and thus, in this case, map will return an array of those integers). But it just as easily could just be one that returns something else. E.g.,
let numbers = [1, 2, 3]
let strings = numbers.map({ (number: Int) -> String in
return "Value is \(number)"
}
print(strings) // ["Value is 1", "Value is 2", "Value is 3"]
But this is the exact same map function. It is just a question of what closure you supply to it. It is one of the reasons that we use closures, that not only can the application programmer supply their own code to be applied to each element in the array, but they can return whatever type they need for each element, too.
As an aside, consider your example:
let numbers = [1, 2, 3]
let results = numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})
First, we would generally use “trailing closure” syntax, eliminating the parentheses:
let numbers = [1, 2, 3]
let results = numbers.map { (number: Int) -> Int in
let result = 3 * number
return result
}
And you might simplify the closure:
let numbers = [1, 2, 3]
let results = numbers.map { (number: Int) -> Int in
return 3 * number
}
And we might let the compiler infer the parameter and return types:
let numbers = [1, 2, 3]
let results = numbers.map { number in
return 3 * number
}
And we might even use “shorthand argument names”, where $0 refers to the first argument, $1 the second, etc. And, when there is only one line of code, you can even omit the return keyword. E.g.,
let numbers = [1, 2, 3]
let results = numbers.map { $0 * 3 }
These are all equivalent to the example you provided in your question. In practice, one would generally use one of these simplified renditions (or a permutation thereof), reducing the amount of syntactic noise in the code.

how do I get the index from this function

this is demo of iOS Charts library (LineChart) and I want to input my data instead of arc4random data.
My data is in Array so I have to approach with index but I can't understand the (0..<count).map { (i) -> ChartDataEntry code.
func setChartValues(_ count : Int = 24) {
let values = (0..<count).map { (i) -> ChartDataEntry in
let val = Double(arc4random_uniform(UInt32(count))+3)
return ChartDataEntry(x: Double(i), y: val)
}
let set1 = LineChartDataSet(entries: values , label : "DataSet 1")
let data = LineChartData(dataSet: set1)
self.lineChartView.data = data
}
It seems you are new to iOS and swift. What you are looking for is an understanding of the functionning of closures in swift, plus the map function which is called an high order function
from apple doc ( https://developer.apple.com/documentation/swift/array/3017522-map ) :
Returns an array containing the results of mapping the given closure over the sequence’s elements.
In other words it maps your array into another array, according to the trailing closure you passed as a parameter.
In your specific case here his how to read it :
(0..<count) : creates an array of count lengh
example : if count = 4 then (0..<count) is [0, 1, 2, 3]
As said previously the map function will transform each of your element into another ( therefore keeping the length of the array ).
in your case val = Double(arc4random_uniform(UInt32(count))+3) will be equal to a random number calculated with count value, and create a new ChartDataEntry with this random value.
to sum it up the whole code is just saying "I will create a count length array of random ChartDataEntry", I guess as a mockup
I suggest you to read about closures here :
https://medium.com/the-andela-way/closures-in-swift-8aef8abc9474
and high order functions ( such as map(_:) ) here :
https://medium.com/#abhimuralidharan/higher-order-functions-in-swift-filter-map-reduce-flatmap-1837646a63e8
let values = (0.. ChartDataEntry in
let val = Double(arc4random_uniform(UInt32(count))+3)
return ChartDataEntry(x: Double(i), y: val)
}
The value mapped and return is you can say a hash function. (arc4random).
It index you are taking is just setting X axis of the chart like 0 , 1 ,2 etc...
and your graph Y it set according to the functions return (arc4random)

Swift lazy subscript ignores filter

How does subscripting a lazy filter work?
let ary = [0,1,2,3]
let empty = ary.lazy.filter { $0 > 4 }.map { $0 + 1 }
print(Array(empty)) // []
print(empty[2]) // 3
It looks like it just ignores the filter and does the map anyway. Is this documented somewhere? What other lazy collections have exceptional behavior like this?
It comes down to subscripting a LazyFilterCollection with an integer which in this case ignores the predicate and forwards the subscript operation to the base.
For example, if we're looking for the strictly positive integers in an array :
let array = [-10, 10, 20, 30]
let lazyFilter = array.lazy.filter { $0 > 0 }
print(lazyFilter[3]) // 30
Or, if we're looking for the lowercase characters in a string :
let str = "Hello"
let lazyFilter = str.lazy.filter { $0 > "Z" }
print(lazyFilter[str.startIndex]) //H
In both cases, the subscript is forwarded to the base collection.
The proper way of subscripting a LazyFilterCollection is using a LazyFilterCollection<Base>.Index as described in the documentation :
let start = lazyFilter.startIndex
let index = lazyFilter.index(start, offsetBy: 1)
print(lazyFilter[index])
Which yields 20 for the array example, or l for the string example.
In your case, trying to access the index 3:
let start = empty.startIndex
let index = empty.index(start, offsetBy: 3)
print(empty)
would raise the expected runtime error :
Fatal error: Index out of range
To add to Carpsen90's answer, you run into one of Collection's particularities: it's not recommended, nor safe to access collections by an absolute index, even if the type system allows this. Because the collection you receive might be a subset of another one.
Let's take a simpler example, array slicing:
let array = [0, 1, 2, 3, 4]
let slice = array[2..<3]
print(slice) // [2]
print(slice.first) // Optional(2)
print(slice[0]) // crashes with array index out of bounds
Even if slice is a collection indexable by an integer, it's still unsafe to use absolute integers to access elements of that collection, as the collection might have a different set of indices.

Functional way to map a geometric series in Swift

I have an array of size n. I would like to fill it with values from a geometric series with a functional approach.
What function should I use?
The result should be an array such as :
[a, a^2, a^3, ... a^n]
You can use sequence(first:next:) to compute powers
of a by repeated multiplication, limit the (lazily evaluated) sequence with prefix(_:) to the desired number of entries, and then create an array from the truncated sequence. Example:
let a = 0.5 // The base
let n = 4 // The maximal exponent
let series = Array(sequence(first: a, next: { $0 * a }).prefix(n))
print(series) // [0.5, 0.25, 0.125, 0.0625]
Another option can be to enumerate the sequence without creating an
actual array:
for x in sequence(first: a, next: { $0 * a }).prefix(n) {
// do something with `x`
}
You can create such geometric series by simply calling map on a range and doing the power operation inside map.
func createGeometricSeries(ofSize n:Int, _ a:Int)->[Int]{
return (1...n).map({Int(pow(Double(a), Double($0)))})
}
createGeometricSeries(ofSize: 3,2) //[2,4,8]
You can use map for that,
let resultingArray = yourArray.map({ a * $0 })
resulting array is the array that will meet your requirement
You can find more about it here in Apple Documentation.

Adding Values In Dictionary With Swift

I have this Dictionary:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
and I want to add the values for example to have the result as 30 , how can I do that? In other words, how can I only add the numbers, not the words?
Since an answer has been accepted and it isn't a very good one, I'm going to have to give up on the socratic method and show a more thematic way of answering this question.
Given your dictionary:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
You get the sum by creating an array out of the dict.values and reducing them
let sum = Array(dict.values).reduce(0, +)
Or you could use the bare form of reduce which doesn't require the array to be created initially:
let sum = reduce(dict.values, 0, +)
Or the more modern version, since reduce is defined on an Array
let sum = dict.values.reduce(0, +)
The accepted answer doesn't use the power of swift
and the answer that does is outdated.
The simplest updated solution is:
let valuesSum = dict.values.reduce(0, +)
start with zero, and sum the values of all the elements
As explained in the documentations here. You access and modify a dictionary through its methods and properties, or by using subscript syntax. Read the doc.
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
To access a value in your dictionary you can use the subscript syntax:
if let cola = dict["cola"] as? Int { // to read the value
// Do something
}
dict["cola"] = 30 // to change the value
dict["pepsi"] = 25 // to add a new entry to your dictionary
dict["fanta"] = nil // to delete the fanta entry.
to read all the value in your dictionary
var sum = 0
for (drinkName, drinkValue) in dict {
println("\(drinkName): \(drinkValue)")
sum += drinkValue
}
or you can
var sum = 0
for drinkValue in dict.values {
sum += drinkValue
}