【问题标题】:Transform from dictionary to array in swift without a for loop在没有 for 循环的情况下快速从字典转换为数组
【发布时间】:2015-02-22 12:25:11
【问题描述】:

我有一些从服务器返回的数据如下所示:

let returnedFromServer = ["title" : ["abc",  "def",  "ghi"],

                      "time"  : ["1234", "5678", "0123"],

                      "content":["qwerty", "asdfg", "zxcvb"]]

我想把它变成这样的东西:

let afterTransformation =

[["title" : "abc",
  "time"  : "1234",
 "content": "qwerty"],

["title" : "def",
 "time"  : "5678",
"content": "asdfg"],

["title" : "ghi",
 "time"  : "0123",
"content": "zxcvb"]]

我目前的实现如下:

var outputArray = [[String : AnyObject]]()

for i in 0..<(returnedFromServer["time"] as [String]).count {

        var singleDict = [String: AnyObject]()

        for attribute in returnedFromServer {

            singleDict[attribute] = returnedFromServer[attribute]?[i]
        }
        outputArray.append(singleDict)
}

这很好用,但我认为这不是一个非常优雅的解决方案。鉴于 Swift 有一些简洁的功能,例如 reducefilterma​​p,我想知道我是否可以在不显式使用循环的情况下完成相同的工作.

感谢您的帮助!

【问题讨论】:

    标签: arrays swift dictionary functional-programming reduce


    【解决方案1】:

    使用想法和字典扩展

    extension Dictionary {
        init(_ pairs: [Element]) {
            self.init()
            for (k, v) in pairs {
                self[k] = v
            }
        }
    
        func map<OutKey: Hashable, OutValue>(transform: Element -> (OutKey, OutValue)) -> [OutKey: OutValue] {
            return Dictionary<OutKey, OutValue>(Swift.map(self, transform))
        }
    }
    

    来自

    你可以做到这一点

    let count = returnedFromServer["time"]!.count
    let outputArray = (0 ..< count).map {
        idx -> [String: AnyObject] in
        return returnedFromServer.map {
            (key, value) in
            return (key, value[idx])
        }
    }
    

    【讨论】:

      【解决方案2】:

      Martin R 的回答很好,您应该使用它并接受他的回答 :-),但作为替代方案可以考虑:

      在理想情况下,Swift 标准库应该有:

      • 能够从 2 元组数组初始化字典
      • 一个 Zip3 和一个 Zip2(即取 3 个序列并将它们连接成一个 3 元组序列
      • zipWith 的实现(即类似于 Zip3,但不只是将它们组合成对,而是在给定的元组上运行一个函数以将它们组合在一起)。

      如果你有这一切,你可以写下:

      let pairs = map(returnedFromServer) { (key,value) in map(value) { (key, $0) } }
      assert(pairs.count == 3)
      let inverted = zipWith(pairs[0],pairs[1],pairs[2]) { [$0] + [$1] + [$2] }
      let arrayOfDicts = inverted.map { Dictionary($0) }
      

      这将具有对参差不齐的输入具有鲁棒性的好处——它只会生成输入中最短列表的元素(与从一个特定输入列表中进行计数的解决方案不同)。缺点是将其硬编码为 3 的大小,但这可以使用更通用的 zipWith 版本来修复,该版本需要一系列序列(尽管如果你真的希望你的键是字符串并且值是 AnyObjects 不是你必须变得更漂亮的字符串。

      你自己编写这些函数并不是那么难——尽管为这种一次性的情况编写显然需要付出太多的努力,但它们在多种情况下都很有用。如果你有兴趣,我已经在this gist 中放了一个完整的实现。

      【讨论】:

        【解决方案3】:

        我会创建 2 个助手:

        ZipArray(类似于Zip2,但适用于任意长度):

        struct ZipArray<S:SequenceType>:SequenceType {
            let _sequences:[S]
        
            init<SS:SequenceType where SS.Generator.Element == S>(_ base:SS) {
                _sequences =  Array(base)
            }
        
            func generate() -> ZipArrayGenerator<S.Generator> {
                return ZipArrayGenerator(map(_sequences, { $0.generate()}))
            }
        }
        
        struct ZipArrayGenerator<G:GeneratorType>:GeneratorType {
            var generators:[G]
            init(_ base:[G]) {
                generators = base
            }
            mutating func next() -> [G.Element]? {
                var row:[G.Element] = []
                row.reserveCapacity(generators.count)
                for i in 0 ..< generators.count {
                    if let e = generators[i].next() {
                        row.append(e)
                    }
                    else {
                        return nil
                    }
                }
                return row
            }
        }
        

        基本上,ZipArray 翻转“Array of Array”的,例如:

        [
            ["abc",  "def",  "ghi"],
            ["1234", "5678", "0123"],
            ["qwerty", "asdfg", "zxcvb"]
        ]
        

        到:

        [
            ["abc", "1234", "qwerty"],
            ["def", "5678", "asdgf"],
            ["ghi", "0123", "zxcvb"]
        ]
        

        字典扩展:

        extension Dictionary {
            init<S:SequenceType where S.Generator.Element == Element>(_ pairs:S) {
                self.init()
                var g = pairs.generate()
                while let (k:Key, v:Value) = g.next() {
                    self[k] = v
                }
            }
        }
        

        那么你可以:

        let returnedFromServer = [
            "title" : ["abc",  "def",  "ghi"],
            "time"  : ["1234", "5678", "0123"],
            "content":["qwerty", "asdfg", "zxcvb"]
        ]
        
        let outputArray = map(ZipArray(returnedFromServer.values)) {
            Dictionary(Zip2(returnedFromServer.keys, $0))
        }
        

        【讨论】:

          猜你喜欢
          • 2019-02-16
          • 2021-11-18
          • 1970-01-01
          • 1970-01-01
          • 2017-03-25
          • 2019-12-06
          • 1970-01-01
          • 2018-03-21
          • 1970-01-01
          相关资源
          最近更新 更多