I'm attempting to add after transforming a two-dimensional array into a one dimensional array using the following code in a Playground:
let twoDimensionalArray = [[1, 3, 5], [2, 4, 6], [12, 15, 16]]
let oneDimensionalArray = twoDimensionalArray.flatMap { $0.map { $0 += 2 } }
print(oneDimensionalArray)
However I receive the error:
left side of mutating operator isn't mutable: '$0' is immutable
Also I see that the flatmap method is deprecated in the Apple Documentation so what should I be doing differently?
You almost right. All you need is remove =:
let twoDimensionalArray = [[1, 3, 5], [2, 4, 6], [12, 15, 16]]
let oneDimensionalArray = twoDimensionalArray.flatMap { $0.map { $0 + 2 } }
print(oneDimensionalArray) // [3, 5, 7, 4, 6, 8, 14, 17, 18]
You can apply changes to the value ($0) in closure by manipulating with it and something else, not by directly changing (i.e. $0 += 2).
Related
In Swift there is a Form... equivalent for the Sets methods intersection(), symmetricDifference() and union(), i.e. formIntersection(), formSymmetricDifference() and formUnion().
But for the method subtracting() there is no method called formSubtracting. Does anyone know why this is so, because it seams I now have to use something like mySet = mySet.subtracting(anotherSet)
subtract(_:) is what you are looking for:
Removes the elements of the given set from this set.
Example:
var mySet: Set = [1, 2, 3, 4, 5]
let anotherSet : Set = [2, 4, 6, 8]
mySet.subtract(anotherSet)
print(mySet) // [3, 1, 5]
There is also a variant which takes another sequence (of the same element type) as the argument, e.g. an array:
var mySet: Set = [1, 2, 3, 4, 5]
let anotherSequence = [2, 4, 6, 8]
mySet.subtract(anotherSequence)
print(mySet) // [3, 1, 5]
With an unordered array of Ints as such:
let numbers = [4, 3, 1, 5, 2]
Is it possible in Swift, using .sorted { }, to order the array with one item prioritised and placed in the first index of the array. So instead of returning [1, 2, 3, 4, 5] we could get [3, 1, 2, 4, 5]?
You can declare a function like this :
func sort(_ array: [Int], prioritizing n: Int) -> [Int] {
var copy = array
let pivot = copy.partition { $0 != n }
copy[pivot...].sort()
return copy
}
Which uses the partition(by:) function.
You could use it like so:
let numbers = [4, 3, 1, 5, 2]
let specialNumber = 3
sort(numbers, prioritizing: specialNumber) //[3, 1, 2, 4, 5]
Here are some test cases :
sort([3, 3, 3], prioritizing: 3) //[3, 3, 3]
sort([9, 4, 1, 5, 2], prioritizing: 3) //[1, 2, 4, 5, 9]
Here an alternative solution that uses sorted(by:) only :
let numbers = [4, 3, 1, 5, 2]
let vipNumber = 3
let result = numbers.sorted {
($0 == vipNumber ? Int.min : $0) < ($1 == vipNumber ? Int.min : $1)
}
print(result) //[3, 1, 2, 4, 5]
I'm trying to concatenate two observables of collection with concat() operator, however, it's not working as expected.
I've two observables:
let first = Observable<[Int]>.create { observer in
observer.onNext([1, 2])
observer.onCompleted()
return Disposables.create()
}
let second = PublishSubject<[Int]>()
Using concat():
let items = Observable.concat([first, second])
items.subscribe(onNext: {
print($0)
})
second.onNext([3, 4, 5])
Output:
[1, 2]
[3, 4, 5]
What I want:
[1, 2]
[1, 2, 3, 4, 5]
So you don't just want to concat two observables, you also want to concat the arrays that are in the events that are produced by the two observables. You aren't going far enough to get what you want.
Imagine you had two Array<[Int]> instead of two Observable<[Int]>. Concat-ing the two (as in arr1 + arr2) would not produce [[1, 2], [1, 2, 3, 4, 5]], instead it would produce [[1, 2], [3, 4, 5]]. Your Observables are behaving the same way.
To both concat and combine them, you need scan, as in:
let items = Observable.concat([first, second])
.scan([], accumulator: +)
Which will produce two events:
[1, 2]
[1, 2, 3, 4, 5]
FYI, with arrays, we don't have a scan operator, but we can approximate it with reduce. For arrays, it would be:
let arr1: Array<[Int]> = [[1, 2]]
let arr2: Array<[Int]> = [[3, 4, 5]]
let itemsArr = (arr1 + arr2).reduce([], { result, element in
return result + [(result.last ?? []) + element]
})
print(itemsArr)
So given this data
var data =
["groupA":
[1, 2, 3, 4, 5, 6],
"groupB":
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
I want this output:
["groupA":
[0, 0, 0, 0, 1, 2, 3, 4, 5, 6],
"groupB":
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
This is the best answer I was able to come up with but I feel like its lacking as I have access to the accumulator that I want to mutate within the reduce function.
let maxElement = data.reduce(data.first!) { (acc, obj) in
return acc.value.count > obj.value.count ? acc : obj
}
for dict in data {
if dict.value.count < maxElement.value.count {
var mutableValues = dict.value
mutableValues.insert(0, at: 0)
data[dict.key] = mutableValues
}
}
I think I'm not understanding how to best refactor my reduce function.
If, like me, you don't like for loops, how about this:
data.reduce(data.values.reduce(0){max($0,$1.count)})
{ data[$1.0] = Array(repeating:0,count:$0-$1.1.count) + $1.1; return $0}
You can get the maximum count of your arrays and create an array of zeros with the difference to append to the lesser populated arrays as follow:
var dict: [String:[Int]] = ["groupA": [1, 2, 3, 4, 5, 6],
"groupB": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
let maxCount = dict.values.map{ $0.count }.max() ?? 0
for (key, value) in dict {
let difference = maxCount - value.count
if difference > 0 {
dict[key] = repeatElement(0, count: difference) + value
}
}
print(dict) // ["groupB": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "groupA": [0, 0, 0, 0, 1, 2, 3, 4, 5, 6]]
As per your question you need to refactor your code for the reduce function, I did it the following way.
Hope this helps,
let maxElement = data.reduce(data.first!) {
$0.value.count > $1.value.count ? $0 : $1
}
further I modified the code you provided to achieve the results you were trying.
and the code worked well for me.
let maxElement = data.reduce(data.first!) {
$0.value.count > $1.value.count ? $0 : $1
}
let minElement = data.reduce(data.first!) {
$0.value.count < $1.value.count ? $0 : $1
}
for dict in data {
if dict.value.count < maxElement.value.count {
var mutableValues = minElement.value
let arrayOfZeros = Array(repeating: 0, count: maxElement.value.count - minElement.value.count)
mutableValues.insert(contentsOf: arrayOfZeros, at: 0)
data[dict.key] = mutableValues
}
}
I have two arrays of [PFObjects].
For example (simplified):
arr1: [PFObject] = [1, 2, 3, 4, 5, 6, 7, 8]
arr2: [PFObject] = [1, 2, 3, 4, 5]
What is the optimal way to compare arr1 with arr2 and only keep the duplicates (remove unique values).
So that arr1 looks like:
arr1 = [1, 2, 3, 4, 5]
let array = arr1.filter { arr2.contains($0) }
voilĂ !
First solution (Looping):
var arr1: [PFObject] = [1, 2, 3, 4, 5, 6, 7, 8]
var arr2: [PFObject] = [1, 2, 3, 4, 5]
var temp: [PFObject] = []
for element in arr1 {
if contains(arr2, element) {
temp.append(element)
}
}
arr1 = temp
You can loop over the first array, check if each element is contained in the array, if it is, you can add it to a temporary array. After looping over every element you can replace the value of the first array with your temporary array.
Second solution (Sets):
var arr1: [PFObject] = [1, 2, 3, 4, 5, 6, 7, 8]
var arr2: [PFObject] = [1, 2, 3, 4, 5]
let set1 = Set(arr1)
let set2 = Set(arr2)
var arr1= Array(set1.intersect(set2)) // [1, 2, 3, 4, 5]
What you do here is:
First you create sets from your arrays
Then you use the intersect method from sets to determine common elements
Finally you transform your set to an array before passing it back to arr1
Of course since you will be using sets, duplicate elements will be lost but I'm guessing that shouldn't be a problem in your case
Third solution (filter):
From the answer of Pham Hoan you can use filters to obtain a subset of arr1, the closure gives you the conditions, here it is that arr2 contains the value you are looking at.
let array = arr1.filter { arr2.contains($0) }
This is obviously the shorter solution in terms of code length.
I do not know which technique would be more efficient if you have very large arrays however.