-4

How can I merge several arrays into one 2D array?

Given I have the following input:

var arr1 = ["1", "2", "3"]
var arr2 = ["a", "b", "c"]
var arr3 = ["aa", "bb", "cc"]

, I need to have the output like this:

[["1", "a", "aa"], ["2", "b", "bb"], ["1", "c", "cc"]]
Cristik
  • 30,989
  • 25
  • 91
  • 127
  • 2
    You mean `["3", "c", "cc"]` for the last subarray? – Sweeper Sep 23 '18 at 09:21
  • Welcome to StackOverflow. Please show what you've already tried, what didn't work, code samples etc. Please read [ask] and [mcve] and then [edit] your question showing the _minimum_ code that demonstrates the problem. – Ashley Mills Sep 23 '18 at 10:26
  • 1
    Will the subarrays all be of the same length? – Sweeper Sep 23 '18 at 11:33

3 Answers3

2

I think what you want is to combine the three arrays into a 2D array, and then transpose it.

To transpose a 2D array, you can find many solutions in this question.

This uses Crashalot's solution in the comments:

fileprivate func transpose<T>(input: [[T]]) -> [[T]] {
    if input.isEmpty { return [[T]]() }
    let count = input[0].count
    var out = [[T]](repeating: [T](), count: count)
    for outer in input {
        for (index, inner) in outer.enumerated() {
            out[index].append(inner)
        }
    }
    return out

}

var arr1 = ["1", "2", "3"]
var arr2 = ["a", "b", "c"]
var arr3 = ["aa", "bb", "cc"]

transpose(input: [arr1, arr2, arr3])

If you want a more swifty transpose, you can use this (modified from here):

extension Collection where Self.Element: RandomAccessCollection {
    func transposed() -> [[Self.Iterator.Element.Iterator.Element]]? {
        guard Set(self.map { $0.count }).count == 1 else { return nil }

        guard let firstRow = self.first else { return [] }
        return firstRow.indices.map { index in
            self.map{ $0[index] }
        }
    }
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • All sub arrays have to have the same count – ielyamani Sep 23 '18 at 10:16
  • `input` has to have a matrix form. For the transposing to work, all inner arrays have to have the same number of elements. – ielyamani Sep 23 '18 at 10:23
  • @Carpsen90 But OP hasn't said anything about what happens when the arrays are off different lengths, so I didn't handle that. What do you think the output should be when they are different lengths? – Sweeper Sep 23 '18 at 10:25
  • Some padding with a default value might help (Speaking in math terms, all vectors (arrays) do have to have the same number of coordinates, it shouldn't even be allowed) – ielyamani Sep 23 '18 at 10:28
  • `guard input.dropFirst().first(where: {$0.count != input[0].count}) == nil else { fatalError("All arrays have to have the same number of elements") }` – ielyamani Sep 23 '18 at 10:51
  • @Cristik Found a more swifty version of `transpose`. – Sweeper Sep 23 '18 at 11:21
  • @Cristik It should be fine now. I simply rejected anything with subarrays of different lengths, since the OP did not say anything about subarrays having different lengths. – Sweeper Sep 23 '18 at 11:32
0

This is a naive expressive way, if you like.

[arr1,arr2,arr3].reduce([[],[],[]]) { result, next -> [[String]] in
   return (0..<(next.count)).map{
         var array = Array.init(result[$0])
         array.append(next[$0]);
         return array
   }
}

or more directly:

[arr1,arr2,arr3].reduce(into: [[],[],[]]) { ( result : inout [[String]], next) in
 _ = (0..<(next.count)).map{ result[$0].append(next[$0]);}
 }
E.Coms
  • 11,065
  • 2
  • 23
  • 35
0

A variadic, FP-style, solution, that can pack an arbitrary number of arrays:

func pack<T>(_ arrays: [T]...) -> [[T]] {
    guard !arrays.isEmpty else { return [] }
    let minCount = arrays.map(\.count).min()!
    return (0..<minCount).map { i in arrays.map { $0[i] } }
}

Usage:

let arr1 = ["1", "2", "3"]
let arr2 = ["a", "b", "c"]
let arr3 = ["aa", "bb", "cc"]

let result = pack(arr1, arr2, arr3) 
// [["1", "a", "aa"], ["2", "b", "bb"], ["1", "c", "cc"]]

let anotherResult = pack(["firstName", "lastName"], ["John", "Doe"])
// [["firstName", "John"], ["lastName", "Doe"]]
Cristik
  • 30,989
  • 25
  • 91
  • 127