【问题标题】:Flatten [Any] Array Swift展平 [任何] 数组 Swift
【发布时间】:2018-05-12 16:24:10
【问题描述】:

使用this Stack Overflow 问题我有以下代码。

let numbers = [1,[2,3]] as [Any]
var flattened = numbers.flatMap { $0 }
print(flattened) // [1, [2, 3]]

我希望将其设置为[1, 2, 3],而不是设置为[1, [2, 3]]

在 Swift 中实现这一目标的最简单/最简洁的方法是什么?

【问题讨论】:

  • flatMap 适用于数组数组,而不是 Any 数组。
  • @rmaddy 是的,我想通了,现在这对我来说很有意义。反正有没有达到我想要的? flatMap 有什么替代品可以帮助我实现我想要的吗?
  • 这里可能的解决方案:stackoverflow.com/questions/42587629/…
  • 认为 flatMap 应该这样工作有那么不合理吗?如果您想展平一棵树,您可以从一个数组开始,该数组包含每个节点的节点和子数组。我认为这不需要延期。无论如何,感谢您的解决方案!

标签: arrays swift


【解决方案1】:
extension Collection where Element == Any {
    var joined: [Any] { flatMap { ($0 as? [Any])?.joined ?? [$0] } }
    func flatMapped<T>(_ type: T.Type? = nil) -> [T] { joined.compactMap { $0 as? T } }
}

let objects: [Any] = [1,[2,3],"a",["b",["c","d"]]]
let joined = objects.joined()   // [1, 2, 3, "a", "b", "c", "d"]

let integers = objects.flatMapped(Int.self)  // [1, 2, 3]
// setting the type explicitly
let integers2: [Int] = objects.flatMapped()        // [1, 2, 3]
// or casting
let strings = objects.flatMapped() as [String]     // ["a", "b", "c", "d"]

【讨论】:

  • 我可以对字符串做同样的事情吗?
  • 如果我正在混合类型(现在没有发生,但对未来的参考感到好奇)那将如何工作?
  • 请注意,lazy 在这两种情况下都是多余的,因为您正在使用返回数组的重载(即急切地评估)。您可以将($0 is T ? [$0 as! T] : []) 改写为($0 as? T).map { [$0] } ?? [],尽管我个人可能会使用switch(例如gist.github.com/hamishknight/eca9b0be62056284ec37c3d49dd7db65)。另外,我个人会让flattened 成为一种方法,因为它不是O(1);虽然我知道你喜欢你的计算属性:)
【解决方案2】:

可能有更好的方法来解决这个问题,但一种解决方案是编写自己的 Array 扩展:

extension Array {
    func anyFlatten() -> [Any] {
        var res = [Any]()
        for val in self {
            if let arr = val as? [Any] {
                res.append(contentsOf: arr.anyFlatten())
            } else {
                res.append(val)
            }
        }

        return res
    }
}

let numbers = [1,[2, [4, 5] ,3], "Hi"] as [Any]
print(numbers.anyFlatten())

输出:

[1, 2, 4, 5, 3, “嗨”]

此解决方案将处理数组的任何嵌套。

【讨论】:

  • 您可以使用 flatMap 表达式实现这一点:self.flatMap{ ($0 as? [Any]).map{ $0.anyFlatten() } ?? [$0] }
  • @Alexander 确实有效,谢谢。但与我不太“光滑”的实现相比,它似乎真的效率低下。
  • 效率低下在哪里?除了闭包的开销(在这种情况下将被完全优化掉)
  • @Alexander 在我的答案中使用numbers 数组进行的操场测试中,操场显示我的return 语句被调用了3 次。当我使用您的 anyFlatten 方法的单行实现时,操场显示 return 被调用 13 次以获取相同的输入。基于此,它似乎效率低下。但我没有做过任何严格的测试。
  • 如果我正确理解了你的测试方法,我认为这是非常错误的。您的代码的 return 出现在每个递归调用的末尾。 return 语句被调用的次数等于所有子数组的深度之和。在我的代码中,return 具有代码中 .append(contentsOf:)append(_:) 调用的效果。扁平化Array(0..&lt;10)(10 个元素,所有深度为 1)将为我调用 10 次 return(10 个元素),为您调用一次(最大深度为 1),但该计数测量完全不同的东西
【解决方案3】:

这是@rmaddy 的anyFlatten 的替代实现:

最简洁的写法可以是这样,但它相当神秘:

extension Array {
    func anyFlatten() -> [Any] {
        return self.flatMap{ ($0 as? [Any]).map{ $0.anyFlatten() } ?? [$0] }
    }
}

这里有一个更合理的实现:

extension Array {
    func anyFlatten() -> [Any] {
        return self.flatMap{ element -> [Any] in
            if let elementAsArray = element as? [Any] { return elementAsArray.anyFlatten() }
            else { return [element] }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2018-03-26
    • 2019-03-05
    • 2016-09-10
    • 2014-08-19
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    相关资源
    最近更新 更多