快速拆分数组的优雅方式

Man*_*nel 12 arrays swift

给定任何类型的数组和想要的子数组数量,我需要这个输出:

print([0, 1, 2, 3, 4, 5, 6].splitInSubArrays(into: 3))
// [[0, 3, 6], [1, 4], [2, 5]]
Run Code Online (Sandbox Code Playgroud)

即使没有“足够”的元素来填充这些,输出也必须包含正确数量的子数组:

print([0, 1, 2].splitInSubArrays(into: 4))
// [[0], [1], [2], []]
Run Code Online (Sandbox Code Playgroud)

我现在有这个工作实现,但有没有更好(更优雅)的方式来实现这个输出:

extension Array {

    func splitInSubArrays(into size: Int) -> [[Element]] {

        var output: [[Element]] = []

        (0..<size).forEach {

            var subArray: [Element] = []

            for elem in stride(from: $0, to: count, by: size) {
                subArray.append(self[elem])
            }

            output.append(subArray)
        }

        return output
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*n R 15

您可以用一个map()操作替换两个循环:

extension Array {
    func splitInSubArrays(into size: Int) -> [[Element]] {
        return (0..<size).map {
            stride(from: $0, to: count, by: size).map { self[$0] }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

外部map()将每个偏移量映射到相应的数组,内部map()将索引映射到数组元素。

例子:

print([0, 1, 2, 3, 4, 5, 6].splitInSubArrays(into: 3))
// [[0, 3, 6], [1, 4], [2, 5]]

print([0, 1, 2].splitInSubArrays(into: 4))
// [[0], [1], [2], []]
Run Code Online (Sandbox Code Playgroud)

  • 啊,我只是说“剩余”。 (2认同)

Leo*_*bus 6

只是为了好玩一个也适用于字符串的通用实现:

extension Collection {
    func every(n: Int, start: Int = 0) -> UnfoldSequence<Element,Index> {
        sequence(state: dropFirst(start).startIndex) { index in
            guard index < endIndex else { return nil }
            defer { index = self.index(index, offsetBy: n, limitedBy: endIndex) ?? endIndex }
            return self[index]
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
extension RangeReplaceableCollection {
    func splitIn(subSequences n: Int) -> [SubSequence] {
        (0..<n).map { .init(every(n: n, start: $0)) }
    }
}
Run Code Online (Sandbox Code Playgroud)
[0, 1, 2, 3, 4, 5, 6].splitIn(subSequences: 3)   // [[0, 3, 6], [1, 4], [2, 5]]
[0, 1, 2].splitIn(subSequences: 4)               // [[0], [1], [2], []]
"0123456".splitIn(subSequences: 3)               // ["036", "14", "25"]
Run Code Online (Sandbox Code Playgroud)


Fat*_*tie 5

KISS,算法匹配方法:

最直观的方法是非常简单:

  • 对于每个索引
  • 除以三得到余数
  • 将数字放入该数组中

所以它真的无非是这样:

arrays[i%n].append(item i)
Run Code Online (Sandbox Code Playgroud)

下面每个@LeoDabus 注释的示例代码

extension RangeReplaceableCollection {
    func moduloishtrancheization(n: Int) -> [SubSequence] {
        var r: [SubSequence] = .init(repeating: .init(), count: n)
        var i = 0
        forEach {
            r[i%n].append($0)
            i += 1
        }
        return r
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是整件事。


Jes*_*ssy 5

'允许它用于所有序列会很好。

stride(from: 0, through: 6, by: 1).splitInSubArrays(into: 3)
Run Code Online (Sandbox Code Playgroud)

(如果它在许多应用程序中有用,也可以将其放入公共扩展程序中,如下所示。)

extension Sequence {
  func splitInSubArrays(into size: Int) -> [[Element]] {
    enumerated()
      .grouped { $0.offset % size }
      .map { $0.map(\.element) }
  }
}
Run Code Online (Sandbox Code Playgroud)
  /// Group the elements by a transformation into an `Equatable`.
  /// - Note: Similar to `Dictionary(grouping values:)`,
  /// but preserves "key" ordering, and doesn't require hashability.
  func grouped<Equatable: Swift.Equatable>(
    by equatable: (Element) throws -> Equatable
  ) rethrows -> [[Element]] {
    try reduce(into: [(equatable: Equatable, elements: [Element])]()) {
      let equatable = try equatable($1)

      if let index = ( $0.firstIndex { $0.equatable == equatable } ) {
        $0[index].elements.append($1)
      } else {
        $0.append((equatable, [$1]))
      }
    }.map(\.elements)
  }
Run Code Online (Sandbox Code Playgroud)


Cri*_*tik 5

为了完整reduce起见,这是一个适用于所有Collection类型的基于 - 的解决方案:

extension Collection {
    func splitInSubArrays(_ size: Int) -> [[Element]] {
        enumerated().reduce(into: [[Element]](repeating: [], count: size)) {
            $0[$1.offset % size].append($1.element)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

函数如何工作:它创建一个空的[Element]条目数组,并将原始数组的每个元素附加到相应的子数组。我们在这里使用 ofreduce只是为了携带结果数组,以避免显式创建局部变量(尽管内部reduce为我们这样做)。

用法:

print([0, 1, 2, 3, 4, 5, 6].splitInSubArrays(3)) // [[0, 3, 6], [1, 4], [2, 5]]
print([0, 1, 2].splitInSubArrays(4))             // [[0], [1], [2], []]
print("ABCDEF".splitInSubArrays(3))              // ["A", "D"], ["B", "E"], ["C", "F"]]
Run Code Online (Sandbox Code Playgroud)

请注意,正如 Leo Dabus 所指出的,在上面的最后一个示例中,二维数组不是基于字符串的数组,而是二维字符数组[[Character]]。改为生成子串数组,RangeReplaceableCollection可以进行扩展,结果类型可以改为[SubSequence].