【问题标题】:Every Value Combination of Dictionary字典的每个值组合
【发布时间】:2020-09-28 00:54:51
【问题描述】:

绞尽脑汁,似乎无法想出一个优雅的解决方案。想知道是否有人可以帮助我。

我有一个 Swift 字典,每个键都有一个值数组,就像这样......

[A:[1, 2, 3], B:[4, 5, 6], C:[7, 8, 9]]

我需要将它转换成一个字典数组,每个值的组合都像这样......

[
[A:1, B:4, C:7],
[A:1, B:4, C:8],
[A:1, B:4, C:9],
[A:1, B:5, C:7],
[A:1, B:5, C:8],
[A:1, B:5, C:9],
[A:1, B:6, C:7],
[A:1, B:6, C:8],
[A:1, B:6, C:9],
[A:2, B:4, C:7],
[A:2, B:4, C:8],
[A:2, B:4, C:9],
[A:2, B:5, C:7],
[A:2, B:5, C:8],
[A:2, B:5, C:9],
[A:2, B:6, C:7],
...
[A:3, B:6, C:9],
]

【问题讨论】:

  • 这就像计数:您可以通过值的索引对输出进行建模。它们是[0, 0, 0][0, 0, 1][0, 0, 2][0, 1, 0]、...、[2, 2, 2]
  • 所有值的元素数量是否相同?
  • 不,值数组可以有不同的长度。抱歉,我应该指出这一点。
  • 天哪,这显然是一个循环嵌套。
  • ????一个循环巢是为一只循环鸟。 ??????

标签: arrays swift dictionary


【解决方案1】:

这是一个递归解决方案,适用于原始字典中任意数量的键/数组对,以及值数组中的任意数量的值:

let dict = ["A":[1, 2, 3], "B":[4, 5, 6], "C":[7, 8, 9]]

func combine(source: [String : [Int]], partials: [[String : Int]] = [], final: inout [[String : Int]]) {

    if source.isEmpty {
        // base step of recursion
        // there are no more (k:v) pairs in source, so add all of the partials (now final)
        // to the final array
        final.append(contentsOf: partials)
    } else {
        // source has a (k:v) pair, so take the first one
        let (k, vals) = source.first!
        var newsource = source
        
        // remove key from newsource
        newsource[k] = nil
        
        // for each value in the key
        for val in vals {
            // add (k:v) to each partial dictionary
            var newpartials = partials
            
            // If new partials array is empty
            if newpartials.isEmpty {
                // create the array with the first [k:v]
                newpartials = [[k : val]]
            } else {
                // otherwise, add [k:v] to each of the partial dictionaries
                for pidx in newpartials.indices {
                    newpartials[pidx][k] = val
                }
            }
            // recursive call to process the next value in source
            combine(source: newsource, partials: newpartials, final: &final)
        }
        
    }
}

var result = [[String : Int]]()
combine(source: dict, final: &result)
print(result)
print(result.count)

[[“A”:1,“B”:4,“C”:7],[“A”:2,“B”:4,“C”:7],[“A”:3 , "B": 4, "C": 7], ["C": 7, "B": 5, "A": 1], ["C": 7, "B": 5, "A" : 2], ["C": 7, "B": 5, "A": 3], ["C": 7, "B": 6, "A": 1], ["C": 7 , "B": 6, "A": 2], ["C": 7, "B": 6, "A": 3], ["C": 8, "B": 4, "A" : 1], ["C": 8, "B": 4, "A": 2], ["C": 8, "B": 4, "A": 3], ["C": 8 ,“B”:5,“A”:1],[“C”:8,“B”:5,“A”:2],[“C”:8,“B”:5,“A” : 3], ["B": 6, "C": 8, "A": 1], ["B": 6, "C": 8, "A": 2], ["B": 6 , "C": 8, "A": 3], ["A": 1, "C": 9, "B": 4], ["A": 2, "C": 9, "B" : 4], ["A": 3, "C": 9, "B": 4], ["B": 5, "A": 1, "C": 9], ["B": 5 , "A": 2, "C": 9], ["B": 5, "A": 3, "C": 9], ["B": 6, "A": 1, "C" : 9], ["B": 6, "A": 2, "C": 9], ["B": 6, "A": 3, "C": 9]]

27


通用解决方案

没有理由将其仅限于 StringInt。密钥应该是符合Hashable 的任何类型。

要使函数通用,请将签名更改为:

func combine<KEY, VALUE>(source: [KEY : [VALUE]], partials: [[KEY : VALUE]] = [],
    final: inout [[KEY : VALUE]]) where KEY: Hashable {

制作Dictionary extension

@LeoDabus 通过在Dictionary 上将其转换为extension 扩展了此答案,其中ValueCollection(谢谢,Leo!):

extension Dictionary where Value: Collection {
    func permutations() -> [[Key: Value.Element]] {
        guard !isEmpty else { return [] }
        var permutations: [[Key: Value.Element]] = []
        permutate(&permutations)
        return permutations
    }
    private func permutate(_ permutations: inout [[Key: Value.Element]], _ dictionaries: [[Key: Value.Element]] = []) {
        if let (key, value) = first {
            var dictionary = self
            dictionary[key] = nil
            for element in value {
                var dictionaries = dictionaries
                if dictionaries.isEmpty {
                    dictionaries += CollectionOfOne([key: element])
                } else {
                    for index in dictionaries.indices {
                        dictionaries[index][key] = element
                    }
                }
                dictionary.permutate(&permutations, dictionaries)
            }
        } else {
            permutations += dictionaries
        }
    }
}

let dict = ["A":[1, 2, 3], "B":[4, 5, 6], "C":[7, 8, 9]]
let result = dict.permutations()
print(result)
print(result.count)

[[“B”:4,“A”:1,“C”:7],[“B”:4,“A”:2,“C”:7],[“B”:4 ,“A”:3,“C”:7],[“A”:1,“B”:4,“C”:8],[“A”:2,“B”:4,“C” : 8], ["A": 3, "B": 4, "C": 8], ["A": 1, "B": 4, "C": 9], ["A": 2 , "B": 4, "C": 9], ["A": 3, "B": 4, "C": 9], ["C": 7, "A": 1, "B" : 5], ["C": 7, "A": 2, "B": 5], ["C": 7, "A": 3, "B": 5], ["C": 8 , "A": 1, "B": 5], ["C": 8, "A": 2, "B": 5], ["C": 8, "A": 3, "B" : 5], ["C": 9, "A": 1, "B": 5], ["C": 9, "A": 2, "B": 5], ["C": 9 , "A": 3, "B": 5], ["B": 6, "C": 7, "A": 1], ["B": 6, "C": 7, "A" : 2], ["B": 6, "C": 7, "A": 3], ["B": 6, "A": 1, "C": 8], ["B": 6 , "A": 2, "C": 8], ["B": 6, "A": 3, "C": 8], ["B": 6, "A": 1, "C" : 9], ["B": 6, "A": 2, "C": 9], ["B": 6, "A": 3, "C": 9]]

27

【讨论】:

【解决方案2】:

您确定最终需要一系列字典吗?如果没有,这里是一个锯齿状的键值对数组:

let keyValuePairArrays =
  ["A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]]
  .sorted(\.key)
  .map { key, value in
    value.map { (key, $0) }
  }
  .combinations

如果您真的需要字典,只需映射它! ?

keyValuePairArrays.map(Dictionary.init)

支持代码:

public extension Sequence where Element: Sequence {
  var combinations: [[Element.Element]] {
    guard let initialResult = ( first?.map { [$0] } )
    else { return [] }

    return dropFirst().reduce(initialResult) { combinations, iteration in
      combinations.flatMap { combination in
        iteration.map { combination + [$0] }
      }
    }
  }
}
public extension Sequence {
  /// The first element of the sequence.
  /// - Note: `nil` if the sequence is empty.
  var first: Element? {
    var iterator = makeIterator()
    return iterator.next()
  }

  /// Sorted by a common `Comparable` value.
  func sorted<Comparable: Swift.Comparable>(
    _ comparable: (Element) throws -> Comparable
  ) rethrows -> [Element] {
    try sorted(comparable, <)
  }

  /// Sorted by a common `Comparable` value, and sorting closure.
  func sorted<Comparable: Swift.Comparable>(
    _ comparable: (Element) throws -> Comparable,
    _ areInIncreasingOrder: (Comparable, Comparable) throws -> Bool
  ) rethrows -> [Element] {
    try sorted {
      try areInIncreasingOrder(comparable($0), comparable($1))
    }
  }
}

【讨论】:

  • 很抱歉接受一个解决方案,然后再切换。但这是我一直在寻找的“优雅”解决方案。谢谢大家。感谢 Jessy 让我更加关注 Sequence 协议。我需要做得更好。
猜你喜欢
  • 2019-05-04
  • 1970-01-01
  • 2022-10-05
  • 2016-11-05
  • 1970-01-01
  • 2017-10-25
  • 2017-04-17
  • 1970-01-01
  • 2017-03-05
相关资源
最近更新 更多