【问题标题】:How can I interleave two arrays?如何交错两个数组?
【发布时间】:2016-04-29 08:31:50
【问题描述】:

如果我有两个数组,例如

let one = [1,3,5]
let two = [2,4,6]

我想按照以下模式合并/交错数组 [one[0], two[0], one[1], two[1] etc...]

//prints [1,2,3,4,5,6]
let comibned = mergeFunction(one, two)
print(combined)

什么是实现组合功能的好方法?

func mergeFunction(one: [T], _ two: [T]) -> [T] {
    var mergedArray = [T]()
    //What goes here
    return mergedArray
}

【问题讨论】:

  • 希望您不要介意,我更改了标题以更准确地代表您的需求。 +1 顺便说一句
  • 谢谢,这更清楚了!

标签: arrays swift array-merge


【解决方案1】:

如果两个数组的长度相同,那么这是一个可能的解决方案:

let one = [1,3,5]
let two = [2,4,6]

let merged = zip(one, two).flatMap { [$0, $1] }

print(merged) // [1, 2, 3, 4, 5, 6]

这里zip()并行枚举数组并返回一个序列 对(2 元素元组)与每个数组中的一个元素。 flatMap() 从每对创建一个 2 元素数组并将结果连接起来。

如果数组可以有不同的长度,那么你附加 结果中较长数组的额外元素:

func mergeFunction<T>(one: [T], _ two: [T]) -> [T] {
    let commonLength = min(one.count, two.count)
    return zip(one, two).flatMap { [$0, $1] } 
           + one.suffixFrom(commonLength)
           + two.suffixFrom(commonLength)
}

Swift 3 更新:

func mergeFunction<T>(_ one: [T], _ two: [T]) -> [T] {
    let commonLength = min(one.count, two.count)
    return zip(one, two).flatMap { [$0, $1] } 
           + one.suffix(from: commonLength)
           + two.suffix(from: commonLength)
}

【讨论】:

  • flatMap 不会“创建元组”。 zip 创建元组;实际上,它产生了一个元组数组。 flatMap 变平!简单的map 会给[[1, 2], [3, 4], [5, 6]]flatMap 删除了额外的数组级别。
  • 你仍然没有为自己伸张正义。 flatMap“连接结果”,同时删除它创建的数组,只留下两个元素。这是您解决方案的独创性。当然我马上就想到了zip,希望你能用它,但是你在这里做的很厉害,就是做一个小数组,只是为了再次破坏它。您正在通过小数组作为删除元组包装器的一种方式,只留下两个元素。
  • :( 噗,突然 Swift 需要在不解构元组的情况下调用 args。例如:let merge = zip(one, two).flatMap { [$0.0, $0.1] }
  • @Charlesism:是的,但这可能会再次改变。是在 Swift Evolution 邮件列表中讨论过的(例如这里是 lists.swift.org/pipermail/swift-evolution-announce/2017-June/…),我不知道目前的状态是什么。
【解决方案2】:

如果你只是想交错两个数组,你可以这样做:

let maxIndex = max(one.count, two.count)
var mergedArray = Array<T>()
for index in 0..<maxIndex {
    if index < one.count { mergedArray.append(one[index]) }
    if index < two.count { mergedArray.append(two[index]) }
}

return mergedArray

【讨论】:

  • 我会更具体地更新问题,我使用整数作为一个简单的例子,这更多是关于基于数组元素的索引合并两个数组(在可能的情况下一个接一个)跨度>
  • 您仍然可以使用上面的方法,只需编写自定义排序函数而不是使用
  • 我希望根据数组中的位置而不是元素的值来插入,即 [one[0]、two[0]、one[1]、two[1] 等。 ..]
  • 所以你只想交错两个数组?我会更新我的答案。
  • 好吧,flatmap 不会交错项目。您可以在两个数组的元组中使用 map,但我认为这不会更清楚。
【解决方案3】:

使用 Swift 5,您可以使用以下 Playground 示例代码之一来解决您的问题。


#1。使用zip(_:_:)函数和CollectionflatMap(_:)方法

let one = [1, 3, 5, 7]
let two = [2, 4, 6]

let array = zip(one, two).flatMap({ [$0, $1] })
print(array) // print: [1, 2, 3, 4, 5, 6]

苹果states:

如果传递给zip(_:_:) 的两个序列长度不同,则生成的序列与较短序列的长度相同。


#2。使用符合SequenceIteratorProtocol 协议的对象

struct InterleavedSequence<T>: Sequence, IteratorProtocol {

    private let firstArray: [T]
    private let secondArray: [T]
    private let thresholdIndex: Int
    private var index = 0
    private var toggle = false

    init(firstArray: [T], secondArray: [T]) {
        self.firstArray = firstArray
        self.secondArray = secondArray
        self.thresholdIndex = Swift.min(firstArray.endIndex, secondArray.endIndex)
    }

    mutating func next() -> T? {
        guard index < thresholdIndex else { return nil }
        defer {
            if toggle {
                index += 1
            }
            toggle.toggle()
        }
        return !toggle ? firstArray[index] : secondArray[index]
    }

}

let one = [1, 3, 5, 7]
let two = [2, 4, 6]

let sequence = InterleavedSequence(firstArray: one, secondArray: two)
let array = Array(sequence)
print(array) // print: [1, 2, 3, 4, 5, 6]

【讨论】:

  • 如果onetwo 长,无论多少,您的#2 和#3 将从one 中多取一个元素,而不是两个。查看我的答案,了解如何处理。
  • @Jessy 感谢您的评论。我更新了示例以反映这一点。
【解决方案4】:
  /// Alternates between the elements of two sequences.
  /// - Parameter keepSuffix:
  /// When `true`, and the sequences have different lengths,
  /// the suffix of `interleaved`  will be the suffix of the longer sequence.
  func interleaved<Sequence: Swift.Sequence>(
    with sequence: Sequence,
    keepingLongerSuffix keepSuffix: Bool = false
  ) -> AnySequence<Element>
  where Sequence.Element == Element {
    keepSuffix
    ? .init { () -> AnyIterator<Element> in
      var iterators = (
        AnyIterator( self.makeIterator() ),
        AnyIterator( sequence.makeIterator() )
      )
      return .init {
        defer { iterators = (iterators.1, iterators.0) }
        return iterators.0.next() ?? iterators.1.next()
      }
    }
    : .init(
      zip(self, sequence).lazy.flatMap { [$0, $1] }
    )
  }
let oddsTo7 = stride(from: 1, to: 7, by: 2)
let evensThrough10 = stride(from: 2, through: 10, by: 2)
let oneThrough6 = Array(1...6)

XCTAssertEqual(
  Array( oddsTo7.interleaved(with: evensThrough10) ),
  oneThrough6
)

XCTAssertEqual(
  Array(
    oddsTo7.interleaved(with: evensThrough10, keepingLongerSuffix: true)
  ),
  oneThrough6 + [8, 10]
)

【讨论】:

  • 与现有答案stackoverflow.com/a/53842830/341994非常相似。
  • 不,那些同时停止zip。 (或者再一次,这可能永远不是想要的结果。)
  • 我明白了,这是一个重要的区别。你可能想用一些解释来补充你的代码!单独的代码几乎从来没有那么有用。
猜你喜欢
  • 2020-07-09
  • 2020-07-20
  • 1970-01-01
  • 2011-03-01
  • 1970-01-01
  • 2018-09-02
  • 2022-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多