3

How to merge several arrays into one array with alternating values?

ex:

var arr1 = [1, 2, 3, 4, 5]
var arr2 = [a, b, c, d, e, f, g]
var arr3 = [aa, bb, cc, dd]

to

[1, a, aa, 2, b, bb, 3, c, cc, 4, d, dd, 5, e, f, g]

3
  • That's not what I wanted. See result array. Commented Apr 28, 2017 at 13:44
  • 2
    First : are a, c, aa, bb variables? Because you seem to be treating them like string literals. Second: what have you tried? stackoverflow.com/help/how-to-ask Commented Apr 28, 2017 at 13:51
  • @AntonVolkov, your edit used single quotes which are valid in Ruby or Python perhaps, but Swift uses double quotes for Character and String. Commented Apr 28, 2017 at 14:37

6 Answers 6

4

If the elements are of the same type in the 3 arrays, you could compute the maximum size and use flatMap to merge them.

For example:

var arr1 = ["1", "2", "3", "4", "5"]
var arr2 = ["a", "b", "c", "d", "e", "f", "g"]
var arr3 = ["aa", "bb", "cc", "dd","ee"]

let arrays    = [arr1,arr2,arr3]
let merged    = (0..<arrays.map{$0.count}.max()!)
               .flatMap{i in arrays.filter{i<$0.count}.map{$0[i]} }

[EDIT] Or if you want a more generalized solution for any number of arrays of a given type, you could create a function to do it:

func mergeArrays<T>(_ arrays:[T] ...) -> [T]
{ 
  return (0..<arrays.map{$0.count}.max()!)
         .flatMap{i in arrays.filter{i<$0.count}.map{$0[i]} }
}  

let merged = mergeArrays(arr1,arr2,arr3)            

[EDIT2] removed use of zip which simplified the expression a bit.

Keeping the zip approach here for future reference:

func mergeArrays<T>(_ arrays:[T] ...) -> [T]
{ 
   return arrays.reduce([[T]]())
   { zip($0,$1).map{$0+[$1]} + $0.dropFirst($1.count) + $1.dropFirst($0.count).map{[$0]} }
   .flatMap{$0}
}    

It could be somewhat faster. I haven't measured it

Sign up to request clarification or add additional context in comments.

Comments

2

I made a func for you. You could use several different arrays as an argument

var arr1 = [1, 2, 3, 4, 5]
var arr2 = ["a", "b", "c", "d", "e", "f", "g"]
var arr3 = ["aa", "bb", "cc", "dd"]

func combine<T>(arrays:[[T]]) -> [T] {

    let maxCount = arrays.reduce(0) { max($0, $1.count) }

    var result = [T]()

    for i in 0..<maxCount {
        for array in arrays {
            if i < array.count {
                result.append(array[i])
            }
        }
    }

    return result

}

combine(arrays: [arr1,arr2,arr3] as [[Any]])
// or
combine(arrays: [arr2,arr3]) // if type is the same

5 Comments

Rather than using Any you should introduce a type parameter T as func combine<T> (arrays:[[T]]) -> [T] { ... }
nope i shouldn't :) cause there's no generic fixed type "T", arrays might be of different types...
With T it would return the right type if all arrays have the same type. If they're different, call it with let combined = combine(arrays: [arr1, arr2, arr3] as [[Any]]).
Ok, @vacawama you won. Seems it has sense to be done though.
As long as you give Swift something to go on it can figure it out T. For instance this works as well: let result: [Any] = combine(arrays: [arr1,arr2,arr3]). You'd want to use a protocol the arrays all implement or a common superclass would work.
1

Try this:

extension Array {

    //Mutating
    mutating func weave(with array: Array) -> Array {
        precondition(!isEmpty && !array.isEmpty)
        var weavedArray = Array<Element>()
        weavedArray.reserveCapacity(count + array.count)
        var inputArray = array
        for _ in 0..<[count, array.count].min()! {
            weavedArray.append(self.removeFirst())
            weavedArray.append(inputArray.removeFirst())
        }
        let largerArr = largerOf(self, inputArray)
        if largerArr.count != 0 {
            weavedArray.append(contentsOf: largerArr)
        }
        self = weavedArray
        return weavedArray
    }

    //Non-mutating
    func weaved(with array: Array) -> Array {
        precondition(!isEmpty && !array.isEmpty)
        var weavedArray = Array<Element>()
        weavedArray.reserveCapacity(count + array.count)
        var selfArray = self
        var inputArray = array
        for _ in 0..<[count, array.count].min()! {
            weavedArray.append(selfArray.removeFirst())
            weavedArray.append(inputArray.removeFirst())
        }
        let largerArr = largerOf(selfArray, inputArray)
        if largerArr.count != 0 {
            weavedArray.append(contentsOf: largerArr)
        }
        return weavedArray
    }

    internal func largerOf<T>(_ arr1: Array<T>, _ arr2: Array<T>) -> Array<T> {
        switch (arr1.count, arr2.count) {
        case (let a, let b) where a > b: return arr1
        case (let a, let b) where a < b: return arr2
        case (let a, let b) where a == b: return arr1
        default: return arr2
        }
    }
}

Usage

Mutating - .weave(with: )

let odds = [1, 3, 5, 7, 9]
let evens = [2, 4, 6, 8, 10]

odds.weave(with: evens)

print(odds) //prints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(evens) //prints: [2, 4, 6, 8, 10]

Non-Mutating - .weaved(with: )

let odds = [1, 3, 5, 7, 9]
let evens = [2, 4, 6, 8, 10]

let firstTen = odds.weaved(with: evens)

print(firstTen) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(odds) //prints: [1, 3, 5, 7, 9]
priny(evens) //prints: [2, 4, 6, 8, 10]

Hope this helps, if you have any further questions, feel free to ask!

Comments

0

No generic function for that I suppose but implementing it is rather simple:

    var sumArr: [Any] = []
    let max_count = max(arr1.count, arr2.count, arr3.count)
    for i in 0...max_count {
        if arr1.count > i { sumArr.append(arr1[i]) }
        if arr2.count > i { sumArr.append(arr2[i]) }
        if arr3.count > i { sumArr.append(arr3[i]) }
     }

or let's make it more reusable:

    func combineArrays(arrays: [[Any]]) -> [Any] {
        var sumArr: [Any] = []
        guard let max_count = arrays.map({$0.count}).max() else { return [] }
        for i in 0...max_count {
            for arr in arrays {
                if arr.count > i { sumArr.append(arr[i]) }
            }
        }
        return sumArr
    }

4 Comments

Your function should use a type parameter T, rather than Any, given that the elements don't matter and there is no reason to lose the type information by returning Any. func combineArrays<T> (arrays: [[T]]) -> [T] { ... }
No need to use map to get the max count arrays.max(by: {$0.count > $1.count})?.count
While we are nitpicking; skip map and max on the result - this is a problem for reduce as: arrays.reduce(0) { max($0, $1.count) }
@LeoDabus how exactly is your approach better than the one with map?
0

You can try this

var c = 0
var i = 0
var arr1 = [1, 2, 3, 4, 5]
var arr2 = [a, b, c, d, e, f, g]
var arr3 = [aa, bb, cc, dd]
var arr = [] 
while (c < 3) {
    if i < arr1.count {
        arr.append(arr1[i])
    } else {
        c += 1
    }
    if i < arr2.count {
        arr.append(arr2[i])
    } else {
        c += 1
    }
    if i < arr3.count {
        arr.append(arr3[i])
    } else {
        c += 1
    }
    i += 1
}

3 Comments

FYI - c = c+1 can be written as c += 1.
@EgorT No, it's not. You are thinking of the ++ and -- operators.
@rmaddy Oops! My mistake :)
0

This is my solution for you :

const arr1 = [1, 2, 3, 4, 5]
const arr2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
const arr3 = ['aa', 'bb', 'cc', 'dd']

const displayAlternatedValues = (a, b, c) => {
  const allArraysInOne = [a, b, c]
  let arrayOfLengths = []
  let arrayOfAlternatedValues = []

  for (let i = 0; i < allArraysInOne.length; i++) {
    const findMaxLength = arrayOfLengths.push(allArraysInOne[i].length)
  } 

  const maxLength = Math.max(... arrayOfLengths)
  // this function allows us to find the length of iteration for the next 'for'

  for (let i = 0; i < maxLength; i++) {
    const associateAlternedValues = [a[i], b[i], c[i]]
    const pushAlternedValues = arrayOfAlternatedValues.push(...associateAlternedValues)
  }
  // this function allows us to create a new array with values sorted by their index

  const cleanedAlternedValues = arrayOfAlternatedValues.filter(elem => typeof elem !=='undefined')

  // this variable create a clean array, without displaying 'undefined' in case there 
  // is no value matching a[i], b[i], c[i]

  return cleanedAlternedValues

}

displayAlternatedValues(arr1, arr2, arr3)

// result : [ 1, 'a', 'aa', 2, 'b', 'bb', 3, 'c', 'cc', 4, 'd', 'dd', 5, 'e', 'f', 'g' ]

Hope this will help ! I'm still learning !

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.