5

I have the following dictionary:

let dict = ["key1": "v1", "key2": "v1", "key3": "v2"]

I want to swap the values for keys so that the result to be:

result = ["v1": ["key1", "key2"], "v2": ["key3"]]

How can I do this without using for loops (i.e. in a functional way)?

Teodor Ciuraru
  • 3,417
  • 1
  • 32
  • 39
  • 1
    Use a `for` loop. – Paulw11 Mar 03 '18 at 19:44
  • https://stackoverflow.com/questions/483666/python-reverse-invert-a-mapping – Teodor Ciuraru Mar 03 '18 at 19:46
  • 2
    Even if _you_ don't loop, _someone_ obviously has to loop, so what difference does it make? And "more elegantly" is a nebulous notion, not suited to Stack Overflow questions. – matt Mar 03 '18 at 19:48
  • 1
    @matt, no need to get pedantic on this question. You are aware that some people prefer their code cleaner and they call it "elegant". I don't think you didn't get what I wanted to say. – Teodor Ciuraru Mar 03 '18 at 19:52
  • [Reverse Swift dictionary lookup](https://stackoverflow.com/q/41383937) – jscs Mar 03 '18 at 19:59

3 Answers3

7

In Swift 4, Dictionary has the init(_:uniquingKeysWith:) initializer that should serve well here.

let d = [1 : "one", 2 : "two", 3 : "three", 30: "three"]
let e = Dictionary(d.map({ ($1, [$0]) }), uniquingKeysWith: {
    (old, new) in old + new
})

If you did not have duplicate values in the original dict that needed to be combined, this would be even simpler (using another new initializer):

let d = [1 : "one", 2 : "two", 3 : "three"]
let e = Dictionary(uniqueKeysWithValues: d.map({ ($1, $0) }))
jscs
  • 63,694
  • 13
  • 151
  • 195
6

You can use grouping initializer in Swift 4:

let dict = ["key1": "v1", "key2": "v1", "key3": "v2"]
let result = Dictionary(grouping: dict.keys.sorted(), by: { dict[$0]! })

Two notes:

  1. You can remove .sorted() if the order of keys in resulting arrays is not important.
  2. Force unwrap is safe in this case because we're getting an existing dictionary key as the $0 parameter
Dan Karbayev
  • 2,870
  • 2
  • 19
  • 28
1

This is a special application of the commonly-needed ability to group key-value pairs by their keys.

public extension Dictionary {
  /// Group key-value pairs by their keys.
  ///
  /// - Parameter pairs: Either `Swift.KeyValuePairs<Key, Self.Value.Element>`
  ///   or a `Sequence` with the same element type as that.
  /// - Returns: `[ KeyValuePairs.Key: [KeyValuePairs.Value] ]`
  init<Value, KeyValuePairs: Sequence>(grouping pairs: KeyValuePairs)
  where
    KeyValuePairs.Element == (key: Key, value: Value),
    Self.Value == [Value]
  {
    self =
      Dictionary<Key, [KeyValuePairs.Element]>(grouping: pairs, by: \.key)
      .mapValues { $0.map(\.value) }
  }

  /// Group key-value pairs by their keys.
  ///
  /// - Parameter pairs: Like `Swift.KeyValuePairs<Key, Self.Value.Element>`,
  ///   but with unlabeled elements.
  /// - Returns: `[ KeyValuePairs.Key: [KeyValuePairs.Value] ]`
  init<Value, KeyValuePairs: Sequence>(grouping pairs: KeyValuePairs)
  where
    KeyValuePairs.Element == (Key, Value),
    Self.Value == [Value]
  {
    self.init( grouping: pairs.map { (key: $0, value: $1) } )
  }
}

With that, just flip each key-value pair and go to town.

Dictionary( grouping: dict.map { ($0.value, $0.key) } )