如何交错两个数组?

17
如果我有两个数组,例如:
let one = [1,3,5]
let two = [2,4,6]

我希望能够按照以下模式合并/交错数组 [one[0], two[0], one[1], two[1]等等...]。
//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
希望你不介意,我把标题改成更符合你的需求。顺便加一分。 - Dan Beaulieu
谢谢,现在更清楚了! - Neil Horton
5个回答

34
如果两个数组长度相同,那么这是一个可能的解决方案:
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()函数并行枚举数组,返回一个包含每个数组对应元素的序列,每个元素都是一个两个元素的元组。 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 移除了额外的数组层级。 - matt
1
你还没有充分展示自己的能力。flatMap“连接结果”,同时删除它创建的数组,只留下两个元素。这是你解决问题的独创性所在。当然,我立刻想到了zip,并希望你使用它,但你所做的卓越之处在于创建一个小数组,然后再将其销毁。你通过传递这个小数组来消除元组包装器,只留下两个元素。 - matt
:( 突然间,Swift 要求参数在不解构元组的情况下被调用。 例如:let merged = zip(one, two).flatMap { [$0.0, $0.1] } - original_username
1
@Charlesism:是的,但这可能会再次改变。在Swift进化邮件列表上进行了讨论(例如在此处https://lists.swift.org/pipermail/swift-evolution-announce/2017-June/000386.html),我不知道当前的状态如何。 - Martin R

6
如果你只是想交错两个数组,你可以像下面这样做:
```javascript function interleave(arr1, arr2) { const result = []; for (let i = 0; i < arr1.length || i < arr2.length; i++) { if (i < arr1.length) { result.push(arr1[i]); } if (i < arr2.length) { result.push(arr2[i]); } } return result; } ```
以上代码可以将两个数组交错起来。
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],等等……]。 - Neil Horton
所以您只想交替地合并两个数组?我会更新我的答案。 - Charles A.
我本以为我会祈求一个聪明的函数方法 :( 我会将你的标记为正确的。 - Neil Horton
2
flatMap不会交错处理项目。你可以潜在地在你的两个数组上使用map,但我认为这样做并不更清晰。 - Charles A.
我不同意。我认为Martin R的回答要清晰得多。Swift更喜欢函数式编程而不是命令式编程。 - matt
显示剩余6条评论

2

使用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]

苹果状态:

如果传递给 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]

1
如果 onetwo 长,无论长度差多少,你的 #2 和 #3 将从 one 中获取一个比从 two 中获取的元素多。查看我的答案以了解如何处理这种情况。 - user652038
1
@Jessy,感谢您的评论。我已经更新了示例以反映这一点。 - Imanou Petit

0
  /// 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]
)

非常类似于现有答案 https://dev59.com/wFsW5IYBdhLWcg3wdnBh#53842830。 - matt
不,它们会和 zip 同时停止。 (或者再多一个,这可能永远都不是期望的结果。) - user652038
2
我明白了,这是一个重要的区别。你可能需要在你的代码中加入一些解释性的文字!仅有代码几乎从来不太有用。 - matt

0

这也可以使用sequence(state:next:)来完成(https://developer.apple.com/documentation/swift/sequence(state:next:)

例如:

let seq1 = [1,2,3,4]
let seq2 = [10,20,30,40,50, 60]

// Interleave two sequences that yield the same element type
let result = sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()), next: { iters in
  iters.0 = !iters.0
  return iters.0 ? iters.1.next() : iters.2.next()
})

print(Array(result)) // Prints: [1, 10, 2, 20, 3, 30, 4, 40]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接