How to reorder an array of objects?

If I have an array of objects that I have to reorder according to an array of integers that represent the original position, how would I reorder the array of objects according to the order given by the array of integers? The reordered objects array would logically be a new array.


For example, an array of objects holds [A, B, C, D] and it needs to be rearranged according to an array of integers [2, 1, 0, 3], where the integer 2, for example, refers to object C, which in the objects array is in position 2. In the new order of the objects array, C would be in the first position, because the integer 2 is in the first position in the integers array.

Accepted Answer

zip is very useful here.


var initialArray = ["A", "B", "C", "D"]
var positions = [2, 1, 0, 3]
var new = Array(zip(initialArray, positions))
print("zipped", new)
new = new.sorted(by: { $0.1 < $1.1})
print("sorted", new)
let sortedArray = new.map() { $0.0}
print("sortedArray", sortedArray)


zipped [("A", 2), ("B", 1), ("C", 0), ("D", 3)]

sorted [("C", 0), ("B", 1), ("A", 2), ("D", 3)]

sortedArray ["C", "B", "A", "D"]

Sorting is definitely overkill here. Try this instead:

let initialArray = ["A", "B", "C", "D"]
let positions = [2, 1, 0, 3]
let reorderedArray = positions.map { i in
    initialArray[i]
}
print(reorderedArray)   // ["C", "B", "A", "D"]

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Brilliant, indeed.


Just need some safety check, in case of error such as position = [2, 0, 1, 4]

let reorderedArray = positions.map { (i: Int) -> String in
    if i >= initialArray.count {
        return initialArray[initialArray.count-1]
    } else if i < 0 {
        return initialArray[0]
    } else {
        return initialArray[i]
    }
}

(does not crash with sort)

Thank you everyone Claude31 and eskimo!

hi,


very nice, guys, and so i'll be adding this nice one-liner extension to Array for one of my projects:


extension Array {
  func subArrayOrdered(byIndexes indexes: [Int]) -> [Element] {
    return indexes.filter({ $0 >= 0 && $0 < self.count }).map({ self[$0] })
  }
}


possible usage:


let x = ["A","B","C","D","E","F"]
let indexes = [0, 3, 2, 5, -1, 17]
print(x.subArrayOrdered(byIndexes: indexes))


the output is


["A", "D", "C", "F"]


this is a little more general solution than the original question requested -- it picks out any sub array of the original array, ordered by specific indexes (while ignoring improper indices, and it even allows you to repeat an index, which can be either a bug or a feature). if the index set is a permutation of the index range of the array, then you do indeed get a specific rearrangement of the original array.


regards,

DMG

Thank you DelawareMathGuy.

Two things:

  • If you’re going to generalise this, you should worry about collections where the start index isn’t zero. For example, this code crashes:

    let catchphrase = ["H", "e", "l", "l", "o", " ", "C", "r", "u", "e", "l", " ", "W", "o", "r", "l", "d", "!"]
    let cruel = catchphrase[6..<11]
    let vowelIndexes = [2, 3]
    let vowels = vowelIndexes.map { i in
        cruel[i]
    }

    because

    cruel
    is of type
    ArraySlice
    . That slice has a
    startIndex
    of 6, making 2 and 3 out of bounds. For it to work, you’d need to use
    cruel[cruel.startIndex + i]
    in line 5.

    This problem is particularly tricky for types, like

    Data
    , where the type and the slice type are the same.
  • The Swift Evolution folks are currently working on a standard library addition for sets of indices.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Ok. Thank you very much.

How to reorder an array of objects?
 
 
Q