1

Let's say I have an array of dictionaries, and each contains an array of letters. Like this:

let dicts = [["letters" : ["a","b","c"]],
             ["letters" : ["d","e","f"]]]

What is the most efficient way to create a flattened array of all the letters from all the dictionaries?

3 Answers 3

2

You can use reduce(_:_:) for that.

let array = dicts.reduce([]) { $0 + ($1["letters"] ?? []) }
print(array) // ["a","b","c","d","e","f"]

Edit: As @Hamish suggested a link in comment simplest solution is having less time, so if you are having large amount of data then you can use forEach closure with array.

var result = [String]()
dicts.forEach {      
    result.append(contentsOf: $0["letters"] ?? [])
}
Sign up to request clarification or add additional context in comments.

8 Comments

Awesome... Super bro :D
Great, thanks. I guess this should be a separate question, but how could you do this if your array was of type [String:Any]? Won't allow the '+' operator in this case.
@JamesWhite Then it should go like { $0 + ($1["letters"] as? [String] ?? []) }
Got it. Thanks so much.
Note that this is pretty bad performance-wise; the accumulating array will be needlessly copied at each iteration.
|
2

You can use flatMap() to map each dictionary to the corresponding letters array and join the results:

let dicts = [["letters" : ["a","b","c"]],
             ["letters" : ["d","e","f"]]]

let letters = Array(dicts.flatMap { $0["letters"] }.joined())
print(letters) // ["a", "b", "c", "d", "e", "f"]

This is effective insofar as no additional intermediate arrays are created during the join. As @Hamish pointed out below, the intermediate [[String]] can be avoided with

let letters = Array(dicts.lazy.flatMap { $0["letters"] }.joined())

5 Comments

You could always get rid of the intermediate [[String]] with Array(dicts.lazy.flatMap { $0["letters"] }.joined()) :)
@Hamish: You are right, thanks! However, I could not observe a significant difference in performance when running your test code multiple times.
Ah interesting w.r.t not being able to notice a difference – the difference is definitely there for me in a release build (not so much in debug), although it does become less prominent as the input size grows. Still thought I would mention it though!
@Hamish: Try changing the order of the tests (do the lazy method first). Does that make a difference (it does in my test)? What happens if you execute the tests multiple times (for _ in 1...10 { <your do blocks> }) ?
Ah yes, you're right – it was the ordering of the tests that did it. There's indeed no noticeable difference between them.
1

You can use any one of these. You don't need to specify the key name of the dictionary.

Solution 1

    let array = dicts.reduce([]) { $0 + ($1.values.reduce([]) { $0 + $1 }) }
    print(array) // ["a","b","c","d","e","f"]

Solution 2

    let array = dicts.flatMap { $0.values.joined() }
    print(array) // ["a","b","c","d","e","f"]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.