Home > Net >  How do I 'deal' the contents of an array to 'n' other arrays using Swift?
How do I 'deal' the contents of an array to 'n' other arrays using Swift?

Time:01-30

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
}

CodePudding user response:

Personally I would use reduce(into:).

extension Sequence {
    func deal(into howMany: Int) -> [[Element]] {
        var current = 0
        return self.reduce(into: Array(repeating: [Element](), count: howMany)) { partial, value in
            partial[current].append(value)
            current = (current   1) % howMany
        }
    }
}

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 thinking 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.

CodePudding user response:

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))
  }
}
  •  Tags:  
  • Related