【问题标题】:Merge multiple arrays into one by mixing elements in Swift?通过在Swift中混合元素将多个数组合并为一个?
【发布时间】:2021-09-06 01:09:19
【问题描述】:

例子:

let a = [a1, a2, a3]
let b = [b1, b2]
let c = [c1, c2, c3, c4]

我需要以下结果:

[a1, b1, c1, a2, b2, c2, a3, c3, c4]

a1, b1, ... - 任何相同类型的对象

我目前的解决方案是创建这些数组的可变副本,并按指定顺序在每个数组上依次调用popFirst,直到所有数组都变空。

但是是否有可能通过使用内部 Swift 功能而无需手动迭代来解决此任务?比如像下面的代码:

[a, b, c].map { ... }.filter { ... }.reduce ...

【问题讨论】:

  • 合并后输出是否排序?
  • @TusharSharma 结果 a2 总是以 [a1, b1, c1, a2, ...] 的形式出现。例如,如果 b 数组为空,则结果应为 [a1, c1, a2, ...]。换句话说,结果存储了初始数组中元素的相对顺序,但最后没有应用额外的排序。

标签: arrays swift array-merge


【解决方案1】:

这是一个使用基本 for 循环的解决方案,但它可以灵活地合并数组的数量

func merge<T>(_ arrays: [T]...) -> [T] {
    guard let longest = arrays.max(by: { $0.count < $1.count })?.count else { return [] }
    var result = [T]()
    for index in 0..<longest {
        for array in arrays {
            guard index < array.count else { continue }
            result.append(array[index])
        }
    }
    return result
}

问题示例

print(merge(a, b, c))

[“a1”、“b1”、“c1”、“a2”、“b2”、“c2”、“a3”、“c3”、“c4”]

【讨论】:

    【解决方案2】:

    将此视为“转置”二维数组[a, b, c],除非没有元素,您只需忽略它并继续前进。这让我想起了this question of mine,只是你希望内部数组有不同的大小。

    我们可以通过查找具有最大 count 的内部数组来修改 Alexander 的答案以满足您的需求,并使用它而不是第一个内部数组。我们还将外部 map 更改为 flatMap,因为您希望它变平。

    我们还引入了safe: 下标,以便compactMap 可以用来忽略那些“缺失”的元素。

    extension Collection where Self.Element: RandomAccessCollection {
        
        func transposed() -> [Self.Element.Element] {
            guard let rowWithMaxElems = self.max(by: { $0.count < $1.count }) else { return [] }
            return rowWithMaxElems.indices.flatMap { index in
                self.compactMap { $0[safe: index] }
            }
        }
    }
    
    extension RandomAccessCollection {
        subscript(safe index: Index) -> Element? {
            get {
                indices.contains(index) ? self[index] : nil
            }
        }
    }
    
    let a = [1, 2, 3]
    let b = [4, 5]
    let c = [6, 7, 8, 9]
    
    let result = [a, b, c].transposed()
    print(result)
    

    【讨论】:

      【解决方案3】:

      你可以这样做

      let a = ["a1", "a2", "a3"]
      let b = ["b1", "b2"]
      let c = ["c1", "c2", "c3", "c4"]
      
      let initArr = [a,b,c]
      let maxCount = initArr.max(by: {$0.count < $1.count})?.count ?? 0
      
      let newArr: [String] = (0..<maxCount).flatMap { (index) -> [String]in
          var arr: [String] = []
          _ = initArr.indices.map { (number)in
              if index < initArr[number].count {
                  arr.append(initArr[number][index])
              }
          }
          return arr
      }
      print(newArr) // ["a1", "b1", "c1", "a2", "b2", "c2", "a3", "c3", "c4"]
      
      

      【讨论】:

      • 看来您可以将内部arrmap 替换为compactMap 并在索引超过数组大小时在其中返回nil
      • 是的,我可以。那时我很着急,所以我发布了。这和上面两个ans的逻辑是一样的@VyachaslavGerchicov
      • map 旨在返回修改后的项目的集合,但您忽略其结果并像forEach 一样使用它。那么为什么不正确使用map 或者用forEach 替换它呢?
      • @VyachaslavGerchicov 我更喜欢地图,因为设计用于快速迭代。你应该做一些研究:albinek.com/map-method-vs-for-loop
      【解决方案4】:

      试试这个 -

      let a = ["a1", "a2", "a3"]
      let b = ["b1", "b2"]
      let c = ["c1", "c2", "c3", "c4"]
              
      var d: [String] = []
      let count = max(a.count, b.count, c.count)
      for i in 0..<count {
          if i < a.count { d.append(a[i]) }
          if i < b.count { d.append(b[i]) }
          if i < c.count { d.append(c[i]) }
      }
      print(d)
      

      输出 -

      ["a1", "b1", "c1", "a2", "b2", "c2", "a3", "c3", "c4"]
      

      【讨论】:

      • 如果需求更改为 10 个数组,op 必须添加 10 个 if 块。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      • 1970-01-01
      • 2017-03-23
      • 1970-01-01
      • 2021-03-16
      • 2013-09-26
      • 2014-06-16
      相关资源
      最近更新 更多