Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group an array to match another array

Tags:

swift

I have an array of dates that is grouped by months. I am trying to group another array of values so that it matches the first array. Is that possible?

For example:

array1 = [[1,2,3],[4,5,6]]
array2 = ["one","two","three","four","five","six"]

I would want the second array to be grouped the same as the first array so that they match:

array2 = [["one","two","three"],["four","five","six"]]
like image 683
Lucas Azzopardi Avatar asked May 15 '26 12:05

Lucas Azzopardi


1 Answers

The evolution of an idea...

First, a solution for a 2D array:

If you know your array1 is a 2-dimensional array (array of arrays of elements), you can do this by making array2 into an iterator and using map and compactMap to replace the elements:

let array1 = [[1,2,3],[4,5,6]]
let array2 = ["one","two","three","four","five","six"]

var iter = array2.makeIterator()

let array3 = array1.map { arr in arr.compactMap { _ in iter.next() }}
print(array3)

Result:

[["one", "two", "three"], ["four", "five", "six"]]

A more general and generic solution:

Here is a more general solution that uses a sequence instead of array2, that doesn't depend on your knowing ahead of time the layout of array1 or the types of the values of either the array or the sequence:

func remap<S: Sequence>(_ array: [Any], using sequence: S) -> [Any] {
    var iter = sequence.makeIterator()

    func remap(_ array: [Any]) -> [Any] {
        return array.compactMap { value in
            if let subarray = value as? [Any] {
                return remap(subarray)
            } else {
                return iter.next()
            }
        }
    }

    return remap(array)
}

How this works:

The second array or sequence is turned into an iterator called iter which allows us to get the values in order with repeated calls to iter.next().

Then a second recursive version of remap() is used to convert [Any] into [Any] in a depth-first traversal order. compactMap() is used to replace elements of the array. While replacing the elements of the array, if the element is another array, it recursively calls remap() on that array until it finally gets to values which aren’t arrays. If the element is a non-array element, it replaces it with the next value from the iterator which is serving up the elements of the sequence (or second array) in order. We use compactMap instead of map to handle the fact that iter.next() is returning optional values because it could run out of values in which case it returns nil. In that case, remap() will replace the remaining elements with nothing while still maintaining the structure of the first nested array.

Examples:

// replace Ints with Strings
let array1: [Any] = [1, [2, 3], [4, [5, 6]]]
let array2 = ["one","two","three","four","five","six"]

let array3 = remap(array1, using: array2)
print(array3)
["one", ["two", "three"], ["four", ["five", "six"]]]
// replace Strings with Ints
let array4: [Any] = ["a", ["b", "c"], [[["d"]], "e"]]
let array5 = [1, 2, 3, 4, 5]

let array6 = remap(array4, using: array5)
print(array6)
[1, [2, 3], [[[4]], 5]]
// map letters to numbers starting with 5 using a partial range
print(remap(["a", ["b"], ["c", ["d"]]], using: 5...))
[5, [6], [7, [8]]]
// using stride to create a sequence of even numbers
let evens = stride(from: 2, to: Int.max, by: 2)
print(remap([["a", "b"], ["c"], [["d"]]], using: evens))
[[2, 4], [6], [[8]]]
// an example of not enough values in replacement array
print(remap([["a", "b"], ["c"], [["d"]]], using: [1]))
[[1], [], [[]]]
like image 174
vacawama Avatar answered May 17 '26 07:05

vacawama