210

I have a dictionary containing UIColor objects hashed by an enum value, ColorScheme:

var colorsForColorScheme: [ColorScheme : UIColor] = ...

I would like to be able to extract an array of all the colors (the values) contained by this dictionary. I thought I could use the values property, as is used when iterating over dictionary values (for value in dictionary.values {...}), but this returns an error:

let colors: [UIColor] = colorsForColorSchemes.values
                        ~~~~~~~~~~~~~~~~~~~~~^~~~~~~
'LazyBidrectionalCollection<MapCollectionView<Dictionary<ColorScheme, UIColor>, UIColor>>' is not convertible to 'UIColor'

It seems that rather than returning an Array of values, the values method returns a more abstract collection type. Is there a way to get an Array containing the dictionary's values without extracting them in a for-in loop?

1
  • 2
    Swift 5: colorsForColorSchemes.values Commented Apr 23, 2019 at 17:22

7 Answers 7

356

As of Swift 2.0, Dictionary’s values property now returns a LazyMapCollection instead of a LazyBidirectionalCollection. The Array type knows how to initialise itself using this abstract collection type:

let colors = Array(colorsForColorSchemes.values)

Swift's type inference already knows that these values are UIColor objects, so no type casting is required, which is nice!

Sign up to request clarification or add additional context in comments.

6 Comments

you can also cast it as an array with typed objects as well. works great! thanks let colors = [CustomColorObject](colorsForColorSchemes.values)
It looks like in Swift 2.0, Dictionary.values now return a LazyMapCollection as opposed to a LazyBidirectionalCollection. Unfortunately, .array isn't defined on a LazyMapCollection. However, the Array(dictionary.values) way still works fine as the array types knows how to initialize itself from a LazyMapCollection.
Do note that the order of the values might be unexpected. For example [0: "small", 1: "medium", 2: "large"] will probably NOT generate ["small", "medium", "large"]
@SimonBengtsson while that's usually good advice, the swift documentation does indicate that values will be in keypair order, at least as of Swift 4. "When iterated over, values appear in this collection in the same order as they occur in the dictionary's key-value pairs." developer.apple.com/documentation/swift/dictionary/…
The unexpected part is that the keypair order is not the same as what is defined in code as can be seen in the example in the documentation you linked. When iterating and printing the values of the mentioned dictionary ["BR": "Brazil", "GH": "Ghana", "JP": "Japan"] Japan is printed in the middle despite being defined last.
|
92

You can map dictionary to an array of values:

let colors = colorsForColorScheme.map { $0.1 }

Closure takes a key-value tuple from dictionary and returns just a value. So, map function produces an array of values.

More readable version of the same code:

let colors = colorsForColorScheme.map { (scheme, color) in
    return color
}

UPDATE

From Xcode 9.0, dictionary values can be accessed using values property, which conforms to Collection protocol:

let colors = colorsForColorScheme.values

Typically you just want it as an array:

let colors = Array(dict.values)

and that's it.

2 Comments

But actually this is readable way to get array let colors = Array(colorsForColorScheme.values)
@pjuzeliunas since this is the best answer here, I just edited in the latest for 2020. obviously unwind, edit etc! cheers and thanks!
11

Use colorsForColorScheme.map({$0.value})

Comments

10

you can create an extension on LazyMapCollection

public extension LazyMapCollection  {

    func toArray() -> [Element]{
        return Array(self)
    }
}

colorsForColorSchemes.values.toArray() or colorsForColorSchemes.keys.toArray()

Comments

7

Firstly, from the following statement, it seems that your variable(dictionary) name is colorsForColorScheme

var colorsForColorScheme: [ColorScheme : UIColor] = ... 

while you are trying to get the values from colorsForColorSchemes dictionary when you did-

let colors: [UIColor] = colorsForColorSchemes.values

which should give you a compile time error. Anyways I am assuming that you had a typo, and you dictionary's name is colorsForColorSchemes. So, here is the solution-

As mentioned earlier, because of the type inference property in swift, your code can infer that the returned type from the .values function is returning an array of UIColor. However, Swift wants to be type-safe, so when you store the values in the colors array, you need to explicitly define that. For swift 5 and above, now you could just do following-

let colors = [UIColor](colorsForColorSchemes.values)

1 Comment

This is really interesting solution and not resource heavy. Do you have reference for it ? Thanks.
3

You can also use flatMap:

let colors = colorsForColorScheme.values.flatMap { $0 }

1 Comment

This is the most elegant answer IMHO. Note that flatMap is deprecated and now we should use compactMap instead. Thank you !
0

I've found this to be the most useful in Swift 5:

colorsForColorSchemes.allValues

See docs - https://developer.apple.com/documentation/foundation/nsdictionary/1408915-allvalues

1 Comment

This is only for NS dictionary. It's simply d.values . In practice Array(d.values)

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.