Let's start with a single array, like:
Copylet raceResult = ["one", "two", "four"]
If we want to combine each element with an offset counting from 0, we can use Array.enumerated(), along with map.
Copylet numberedRaceResult = raceResult
.enumerated()
.map { offset, element in "\(offset). \(element)" }
for numberedResult in numberedRaceResult {
print(numberedResult)
}
// Prints:
// 0. one
// 1. two
// 2. four
You can see that I didn't call print inside the closure passed to map. You can do this, but it kind of defeats the purpose of map (which is to create an equal-sized output array from the transformed elements of the input array), because the result would be unused. In that case, it makes more sense to just use a for loop or a call to forEach, like @Sh_Khan showed.
To handle a nested array, it's much the same. We can use the same logic as for one array, but apply it to each sub-array.
Copylet raceResults = [
["one", "two", "four"],
["two", "one", "five", "six"],
["two", "one", "four", "ten"],
["one", "two", "four"],
]
let numberedRaceResults = raceResults
.enumerated()
.flatMap { outterOffset, raceResult in
raceResult
.enumerated()
.map { innerOffset, element in "\(outterOffset).\(innerOffset). \(element)" }
}
for numberedResult in numberedRaceResults {
print(numberedResult)
}
// Prints:
// 0.0. one
// 0.1. two
// 0.2. four
// 1.0. two
// 1.1. one
// 1.2. five
// 1.3. six
// 2.0. two
// 2.1. one
// 2.2. four
// 2.3. ten
// 3.0. one
// 3.1. two
// 3.2. four
You'll notice that I used flatMap on the outter array, instead of a simple map. You can change it back and forth and compare the result. In short, flatMap gives you a single flat array of string results, rather than an array of sub-arrays of strings.
Let's start with a single array, like:
Copylet raceResult = ["one", "two", "four"]
If we want to combine each element with an offset counting from 0, we can use Array.enumerated(), along with map.
Copylet numberedRaceResult = raceResult
.enumerated()
.map { offset, element in "\(offset). \(element)" }
for numberedResult in numberedRaceResult {
print(numberedResult)
}
// Prints:
// 0. one
// 1. two
// 2. four
You can see that I didn't call print inside the closure passed to map. You can do this, but it kind of defeats the purpose of map (which is to create an equal-sized output array from the transformed elements of the input array), because the result would be unused. In that case, it makes more sense to just use a for loop or a call to forEach, like @Sh_Khan showed.
To handle a nested array, it's much the same. We can use the same logic as for one array, but apply it to each sub-array.
Copylet raceResults = [
["one", "two", "four"],
["two", "one", "five", "six"],
["two", "one", "four", "ten"],
["one", "two", "four"],
]
let numberedRaceResults = raceResults
.enumerated()
.flatMap { outterOffset, raceResult in
raceResult
.enumerated()
.map { innerOffset, element in "\(outterOffset).\(innerOffset). \(element)" }
}
for numberedResult in numberedRaceResults {
print(numberedResult)
}
// Prints:
// 0.0. one
// 0.1. two
// 0.2. four
// 1.0. two
// 1.1. one
// 1.2. five
// 1.3. six
// 2.0. two
// 2.1. one
// 2.2. four
// 2.3. ten
// 3.0. one
// 3.1. two
// 3.2. four
You'll notice that I used flatMap on the outter array, instead of a simple map. You can change it back and forth and compare the result. In short, flatMap gives you a single flat array of string results, rather than an array of sub-arrays of strings.
Map is used to convert one bunch of type T into things of some other type, X. Like map these Ints to String?s. You should not use map for side-effects, like printing the values, or updating a database etc. It should be a pure function that takes an input and returns an output. "Map these A's into B's". Pure meaning the value of the function only depends on the input, nothing else like the current state of the world, and doesn't change the world either (like printing to the console), so for example, map these int's by the function that adds 2 to them.
In your example:
Copyvar raceResults = [["one","two","four"],["two","one","five","six"],["two","one","four","ten"],["one","two","four"]]
You have an array of "arrays of strings".
You can map that to an array of so long as you have a function that takes "array of string" and turns that into "something else"
Here you map it with the Identity function, the function that just returns its input, which is going to take an array of strings as input and return the exact same array of strings as output:
Copy raceResults.map {
return $0 // Returns first array
}
This does nothing, and the result is the exact same thing as raceResults.
If you want to iterate over all these elements then the function flatMap is handy:
CopyraceResults.flatMap { $0 }.forEach { print($0) }
flatMap is flatten, then map. Flattening an array of arrays is to return an array with all the things 'flattened' one level, so [[1, 2, 3], [4, 5, 6]] -> [1, 2, 3, 4, 5, 6], but the definition of what to flatten means depends on the type of container, for example flatMap on Optional means something else to flatMap on Array.
How to map Array/Dictionary data into custom observed object - Using Swift - Swift Forums
Which is the easiest way to convert arrays into dictionaries in Swift?
Adding an index with functional methods like map, reduce, filter - Pitches - Swift Forums
How can I map an array without starting at the beginning of the array?
Videos
How can I make the first element be the key and the index be the value and so on?
I want to be able to take an array of items where each item has a Bool property, and condense it into an array that only contains the items where the Bool property is set to true.
However, I also want to be able to indicate which index in the original array that I want to start at. So, it would start at the index that I give, and put any items after that index into the new array first, but then go back to the start of the original array, and include the items that came before the index that I gave as well.
Here is an example that I just made in a Playground to demonstrate...
import Foundation
struct Animal {
let name: String
let hasLegs: Bool
}
struct AnimalsWithLegs {
let animals: [Animal]
init(allAnimals: [Animal], startingAt index: Int = 0) {
//Need help here
animals = allAnimals.compactMap { animal in
if animal.hasLegs {
return animal
} else {
return nil
}
}
}
}
let animals = [
Animal(name: "Dog", hasLegs: true),
Animal(name: "Fish", hasLegs: false),
Animal(name: "Cat", hasLegs: true),
Animal(name: "Monkey", hasLegs: true),
Animal(name: "Whale", hasLegs: false),
Animal(name: "Turtle", hasLegs: true),
Animal(name: "Shark", hasLegs: false),
Animal(name: "Fox", hasLegs: true)
]
let animalsWithLegs = AnimalsWithLegs(allAnimals: animals, startingAt: 4)
for animal in animalsWithLegs.animals {
print(animal.name)
}Currently, this example will print "Dog, Cat, Monkey, Turtle, Fox"
Those are all of the animals with legs from the array, so that's great.
However, since I gave a startingAt index of 4 when I called the AnimalsWithLegs() initializer, I would actually like it to print "Turtle, Fox, Dog, Cat, Monkey" instead.
How can I modify the AnimalsWithLegs() initializer to get this result?
Since Swift 4 you can do @Tj3n's approach more cleanly and efficiently using the into version of reduce It gets rid of the temporary dictionary and the return value so it is faster and easier to read.
Sample code setup:
struct Person {
let name: String
let position: Int
}
let myArray = [Person(name:"h", position: 0), Person(name:"b", position:4), Person(name:"c", position:2)]
Into parameter is passed empty dictionary of result type:
let myDict = myArray.reduce(into: [Int: String]()) {
$0[$1.position] = $1.name
}
Directly returns a dictionary of the type passed in into:
print(myDict) // [2: "c", 0: "h", 4: "b"]
Okay map is not a good example of this, because its just same as looping, you can use reduce instead, it took each of your object to combine and turn into single value:
let myDictionary = myArray.reduce([Int: String]()) { (dict, person) -> [Int: String] in
var dict = dict
dict[person.position] = person.name
return dict
}
//[2: "b", 3: "c", 1: "a"]
In Swift 4 or higher please use the below answer for clearer syntax.