0

I have a source array with an unknown number of elements:

let sourceArray = ["A", "B", "C", "D", "E", "F", "G", "H", "I"]

I need to 'deal' those elements out into a given number of new arrays.

For example, given '3' empty arrays, the result would look like this:

let result = [["A", "D", "G"],["B", "E", "H"],["C", "F", "I"]]

This is what I've come up with, which technically works but feels pretty clunky. Any suggestions for improvement?

func createArrays(sourceArray: [String], count: Int) -> [[String]] {
    var resultArrays = [[String]]()
    
    for i in 0..<count {
        var newArray = [String]()
        var currentIndex = i
        for (index, item) in sourceArray.enumerated() {
            if index == currentIndex {
                newArray.append(item)
                currentIndex = currentIndex + count
            }
        }
        resultArrays.append(newArray)
    }
    
    return resultArrays
}
mralexhay
  • 1,164
  • 1
  • 10
  • 16

2 Answers2

2

Personally I would use reduce(into:).

extension Sequence {
    func deal(into howMany: Int) -> [[Element]] {
        self.enumerated().reduce(into: Array(repeating: [Element](), count: howMany)) {
            $0[$1.offset % howMany].append($1.element)
        }
    }
}

Now you just say

let output = sourceArray.deal(into: 3)

or however many piles you want. It isn't very sophisticated but I like it because it does exactly what we intuitively would think of as dealing: it literally deals out the elements into piles, one at a time, exactly as you physically do when you deal out a deck of cards into hands, one card per pile round in a circle until the deck is exhausted.

matt
  • 515,959
  • 87
  • 875
  • 1,141
1

Alexander's recommendation is one way to tackle the problem, but striding by the share count, once for each share, yields the same result as chunking and then transposing (except that transposing isn't defined when the chunks are not of equal counts).

["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"].distributedUniformly(shareCount: 4) as Array
[["A", "E", "I"], ["B", "F", "J"], ["C", "G"], ["D", "H"]]
import Algorithms

public extension Sequence {
  /// Distribute the elements as uniformly as possible, as if dealing one-by-one into shares.
  /// - Note: Later shares will be one smaller if the element count is not a multiple of `shareCount`.
  @inlinable func distributedUniformly(shareCount: Int)
  -> LazyMapSequence<Range<Int>, StrideSequence<DropFirstSequence<Self>>> {
    (0..<shareCount).lazy.map {
      dropFirst($0).striding(by: shareCount)
    }
  }
  
  /// Distribute the elements as uniformly as possible, as if dealing one-by-one into shares.
  /// - Note: Later shares will be one smaller if the element count is not a multiple of `shareCount`.
  @inlinable func distributedUniformly(shareCount: Int) -> [[Element]] {
    .init(distributedUniformly(shareCount: shareCount).map(Array.init))
  }
}