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]()) {
1.position] = $1.name
}
Directly returns a dictionary of the type passed in into:
print(myDict) // [2: "c", 0: "h", 4: "b"]
Answer from possen on Stack OverflowSince 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]()) {
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.
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.
If you want to keep that syntax, just use a (mutable) temporary variable
gnomes = gnomes.map { (gnome: Gnome) -> Gnome in
var mutableGnome = gnome
mutableGnome.age = 140
return mutableGnome
}
(Below follows the case where Gnome is a reference type; a class -- since you haven't showed us how you've defined Gnome. For the case where Gnome as value type (a struct), see @vadian:s answer)
The removal of var will not effect using .map to mutate mutable members of an array of reference type objects. I.e., you could simply use your old approach (omitting however, the var in the .map closure signature).
class Gnome {
var age = 42
}
var gnomes = [Gnome(), Gnome(), Gnome()]
gnomes = gnomes.map {
$0.age = 150
return $0
}
/* result */
gnomes.forEach { print($0.age) } // 3x 150
However, in case you just want to modify your original array rather than assigning the result of .map to a new array, .forEach might be a more appropriate choice than .map.
gnomes.forEach { $0.age = 140 }
/* result */
gnomes.forEach { print($0.age) } // 3x 140
You could use the Swift map(_:) function.
let devices = customManager.endpoints.map {
{ name: $0.name, uniqueID: $0.uniqueID }
}
You need to add the type of the endpoints variable to your question. I gather it is of type Endpoint? So edit your question and show us the definition of EndPoint.
Let's say it's a struct:
struct Endpoint {
let Name: String
let uniqueID: Int
}
You could add an extension to the Endpoint type that makes it conform to CustomStringConvertible:
extension EndPoint: CustomStringConvertible {
var description: String {
return "{name: \(name), uniqueID: \(uniqueID)}"
}
}
And then use something like this:
let descriptions = customManager.endpoints.map{$0.description}
.joined(separator: ", ")
print((descriptions)
There are 2 similar key functions which perform similar operations, the basic purpose of which is to take an array and build another array from it:
func map(transform:(R)->T) -> [T] --- Map takes an array of elements of one type and converts it to an array of elements of (potentially) another type, by calling a transform function on each element in turn. So you can convert an array of Int's to an array of strings:
[1, 2, 3, 4].map { "\($0)" } // --> ["1", "2", "3", "4"]
func filter(predicate:(T)->Boolean) -> [T] -- Filter takes an array of elements and converts it to an array of elements of the same type, but only includes those elements for which predicate returns true. So you can filter an array of ints to leave only the even numbers:
[1, 2, 3, 4].filter { $0 % 2 == 0 } // --> [ 2, 4]
There are other variants, such as flatMap which takes [[T]] and turns it into [T] by iterating over the input and array and appending the contents of each array to an output array:
[ [1, 2], [3, 4]].flatMap() // --> [1, 2, 3, 4]
It's also worth nothing that the concept behind map is that, in simplistic terms, it can be used to map any input type to an output type, so you can define:
func <R, T> map(in:R?, transform:(R)->T) -> T?
for example, which would translate any optional input type into an optional output type given a function that translates the base type.
The problem is $0.state = .Flat is an assignment. It does not return a value. Try this:
wheels = wheels.map { w in
w.state = .Flat
return w
}
map does not replace anything. It projects each element from your array to a new array by applying the transformation block. You can choose to assign this new array to the old array, but otherwise it will not alter the original array.