I'm trying to build a closure that does what my title says. My code runs but it does not print what I expected.
var names: [String] = []
var namesAndAges = ["Tom": 25, "Michael": 35, "Harry": 28, "Fabien": 16]
var ofAge = namesAndAges.filter { namesAndAges in namesAndAges.value > 18 }
var addNames = ofAge.map { ofAge in names.append(ofAge.key) }
print(addNames) //this prints [(), (), ()]
Basically you are misusing filter and map methods.
Try this:
var names: [String] = []
var namesAndAges = ["Tom": 25, "Michael": 35, "Harry": 28, "Fabien": 16]
var ofAge = namesAndAges.filter { $0.value > 18 }
var addNames = ofAge.map { $0.key }
print(addNames) //this prints ["Michael", "Harry", "Tom"]
The issue you're experience is because names.append(ofAge.key) returns Void (a.k.a. the empty tuple, ()). map produces a new Array containing the return values of the given closure after it's been applied to every element of the source array (ofAge, in your example).
The names array will actually contain the data you want, but this isn't how map is meant to be used (you're essentially using it in place of forEach).
Try this instead:
var namesAndAges = ["Tom": 25, "Michael": 35, "Harry": 28, "Fabien": 16]
var namesOfAge = namesAndAges.filter { $0.value > 18 }.map{ $0.key }
print(namesOfAge)
To avoid looping more than once, this is how I do it:
var namesAndAges = ["Tom": 25, "Michael": 35, "Harry": 28, "Fabien": 16]
let addNames: [String] = namesAndAges.flatMap { $0.value > 18 ? $0.key : nil }
print(addNames) // prints ["Michael", "Harry", "Tom"]
(oh and it's shorter 🙃)
Related
CODE:
void func({String value}) async {
final QuerySnapshot snapshot = await _firestore
.collection('users')
.where("id", isEqualTo: signedInUser.id)
.get();
snapshot.docs.forEach((element) {
List<dynamic> arr = element.data()["array"];
print(arr);
}
}
OUTPUT:
I/flutter (27759): [10, 20, 30, 33, 34, 24]
I/flutter (27759): [2, 42, 45, 23, 85, 51]
I/flutter (27759): [32, 53, 12, 67, 34, 23]
This is what I get when I print the list from Firebase document.
How do I add items of the arrays for each respective indexes? Like; 10 + 2 + 32 for index 0
You should be able to get the List of Lists and then using a for loop sum the data like:
var lists = snapshot.docs.map((e) => e.data()['array']);
var sums = <int>[];
for (int i = 0; i < lists.first.length; i++) {
var sum = 0;
for (var list in lists) {
sum += list[i];
}
sums.add(sum);
}
Be aware that this code is not tested, and you need to make sure that the lists are of equal length or the code might not work as you expect/throw an exception.
I have a dictionary with two Int’s A:B, and I want create a new dictionary that includes B as an index (with no repetition) and A as a value (only for repeated B’s):
var myDict : [Int:Int] = [12:2345, 14:2345, 99:1111, 67:1111, 77:7657, 132:3345, 199:6778]
Desired output:
var newDict: [Int:[Int]] = [2345: [ 12 , 14 ] , 1111: [ 99 , 67] ]
Note: the original dictionary contains over a thousand entries.
You loop through the first dict by enumerating it, that way you can switch the values in to the new dict
var newDict: [Int:[Int]] = [:]
let myDict : [Int:Int] = [12:2345, 14:2345, 99:1111, 67:1111, 77:7657, 132:3345, 199:6778]
for values in myDict.enumerated() {
var newValue = newDict[values.element.value] ?? []
newValue.append(values.element.key)
newDict[values.element.value] = newValue
}
newDict = newDict.filter { (key, value) -> Bool in
value.count > 1
}
Here is the power of swift:
let newDict = Dictionary(grouping: myDict, by: {$0.value}).filter({$0.value.count > 1}).mapValues({$0.map({$0.key})})
print(newDict)
Output: [1111: [67, 99], 2345: [12, 14]]
Please use this code:
var myDict: [Int:Int] = [12:2345, 14:2345, 99:1111, 67:1111, 77:7657, 132:3345, 199:6778]
let values = myDict.values
let tempValueSet = Set<Int>(values)
let uniqueValues = Array(tempValueSet)
var result = [Int: [Int]]()
for item in uniqueValues {
result[item] = myDict.allKeys(forValue: item)
}
print(result)
And this is Dictionary extension:
extension Dictionary where Value: Equatable {
func allKeys(forValue val: Value) -> [Key] {
return self.filter { $1 == val }.map { $0.0 }
}
}
Desired output:
[6778: [199], 1111: [99, 67], 7657: [77], 3345: [132], 2345: [12, 14]]
For more reference about this extension : https://stackoverflow.com/a/27218964/2284065
If you don't want to use extension then you can use this code too :
var myDict: [Int:Int] = [12:2345, 14:2345, 99:1111, 67:1111, 77:7657, 132:3345, 199:6778]
let values = myDict.values
let tempValueSet = Set<Int>(values)
let uniqueValues = Array(tempValueSet)
var result = [Int: [Int]]()
for item in uniqueValues {
result[item] = (myDict as NSDictionary).allKeys(for: item) as! [Int]
}
print(result)
I have an array of ages.
I want to split the array in to 4 subarrays by the age value.
A -> 0...25
B -> 26...50
c -> 51...75
d -> 76 +
I have no problem iterating through the array and append to different arrays by the age value.
let subArrays: [[Int]] = [[], [], [], []]
for age in ages {
switch age {
case 0...25:
subArrays[0].append(age)
case 26...50:
subArrays[1].append(age)
case 51...75:
subArrays[2].append(age)
default:
subArrays[3].append(age)
}
}
My questions is:
Is there a cleaner way to do this using map, split or any other function.
Thanks
This doesn't use map or anything fancy but you can easily eliminate the switch:
var subArrays: [[Int]] = [[], [], [], []]
for age in ages {
subArrays[min((age - 1) / 25, 3)].append(age)
}
The use of min ensures all values of 76 and greater go into the last slot.
And to test, with all boundary cases, try:
let ages = [ 50, 67, 75, 76, 25, 12, 26, 51, 99, 45, 0, 120, 16 ]
And then:
print(subArrays)
Gives:
[[25, 12, 0, 16], [50, 26, 45], [67, 75, 51], [76, 99, 120]]
A more generic version that does not depend on any mathematical property of your ranges:
func split<T>(array: [T], ranges: [CountableClosedRange<T>]) -> [[T]] {
var result = Array(repeating: [T](), count: ranges.count)
for element in array {
if let subIndex = ranges.index(where: { $0 ~= element }) {
result[subIndex].append(element)
}
}
return result
}
let ages = [10, 20, 30, 40, 50, 60]
let subarrays = split(array: ages, ranges: [0...25, 26...50, 51...75, 76...Int.max])
Note that it does not check for overlapping ranges.
Based on rmaddy's answer (practically the same) but within one line:
let subs = (0...3).map { sub in ages.filter { sub == min(($0 - 1) / 25, 3) } }
I want to convert an Array of UInt8 to several strings
var BytesArray = [123, 9, 32, 236]
TO
var Srt0 = "123"
var Srt1 = "9"
var Srt2 = "32"
var Srt3 = "236"
As you say, the answer should be like that-
var BytesArray = [123, 9, 32, 236]
var sort0:String = String(BytesArray[0])
var sort1:String = String(BytesArray[1])
var sort2:String = String(BytesArray[2])
var sort3:String = String(BytesArray[3])
Do you mean the following?
var bytesArray = [123, 9, 32, 236]
let stringArray = bytesArray.map( { "\($0)" })
print(stringArray)
This will produce an array of strings.
var BytesArray: [Int] = [123, 9, 32, 236]
var stringArray = BytesArray.map
{
String($0)
}
stringArray will become ["123", "9", "32", "236"]
I have been trying to run this loop to change the position of an element in an array based but I get the error below. Not really sure what could be wrong. Any thoughts or Ideas!! Thanks.
var population = [[98, 8, 45, 34, 56], [9, 13, 65, 4, 90], [24, 5, 4, 56, 88], [3, 55, 22, 19, 10]]
private func randomIntergerInRange(low:Int, high:Int) -> Int {
let randomGeneratedNumber = low + Int(arc4random()) % (high - low);
return Int(randomGeneratedNumber)
}
for individual in population {
var mutate = 0.01
if mutate > Double(Float(arc4random()) / Float(UINT32_MAX)) {
var positionToMutate = randomIntergerInRange(0, (individual.count - 1))
var valueToAssign = randomIntergerInRange(minElement(individual), maxElement(individual))
individual[positionToMutate] = valueToAssign
print(individual)
}
}
Array are passed by value, so when you use individual at each iteration it is assigned a copy of the subarray of population, and even if you use for var individual in population ... you mutate only a copy of the original array.
If you want to mutate the values of the original array you can do it by using indices, for instance:
for index in population.indices {
var mutate = 0.01
if mutate > Double(Float(arc4random()) / Float(UINT32_MAX)) {
var positionToMutate = randomIntergerInRange(0, (population[index].count - 1))
var valueToAssign = randomIntergerInRange(minElement(population[index]), maxElement(population[index]))
population[index][positionToMutate] = valueToAssign
print(population[index])
}
}
Reason for the error message: the individual object in your loop is immutable, it's a copy of the object, not a reference.
You could do something like this, using enumerate:
for (index, individual) in enumerate(population) {
var mutate = 0.01
if mutate > Double(Float(arc4random()) / Float(UINT32_MAX)) {
var positionToMutate = randomIntergerInRange(0, (individual.count - 1))
var valueToAssign = randomIntergerInRange(minElement(individual), maxElement(individual))
population[index][positionToMutate] = valueToAssign
print(population[index])
}
}
You shouldn't modify a collection while iterating it, btw, it could have side effects.
Update for Swift 2
private func randomIntegerInRange(low:Int, _ high:Int) -> Int {
let randomGeneratedNumber = low + Int(arc4random()) % (high - low)
return Int(randomGeneratedNumber)
}
var mutate = 0.01
for (index, individual) in population.enumerate() {
if mutate > Double(Float(arc4random()) / Float(UINT32_MAX)) {
var positionToMutate = randomIntegerInRange(0, (individual.count - 1))
if let min = individual.minElement(), max = individual.maxElement() {
var valueToAssign = randomIntegerInRange(min, max)
population[index][positionToMutate] = valueToAssign
print(population[index])
}
}
}