How to rearrange item of an array to new position in Swift?

ArraysSwift

Arrays Problem Overview


Consider the array [1,2,3,4]. How can I rearrange the array item to new position.

For example:

put 3 into position 4 [1,2,4,3]

put 4 in to position 1 [4,1,2,3]

put 2 into position 3 [1,3,2,4].

Arrays Solutions


Solution 1 - Arrays

Swift 3.0+:

let element = arr.remove(at: 3)
arr.insert(element, at: 2)

and in function form:

func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
    var arr = array
    let element = arr.remove(at: fromIndex)
    arr.insert(element, at: toIndex)

    return arr
}

Swift 2.0:

This puts 3 into position 4.

let element = arr.removeAtIndex(3)
arr.insert(element, atIndex: 2)

You can even make a general function:

func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
    var arr = array
    let element = arr.removeAtIndex(fromIndex)
    arr.insert(element, atIndex: toIndex)

    return arr
}

The var arr is needed here, because you can't mutate the input parameter without specifying it to be in-out. In our case however we get a pure functions with no side effects, which is a lot easier to reason with, in my opinion. You could then call it like this:

let arr = [1,2,3,4]
rearrange(arr, fromIndex: 2, toIndex: 0) //[3,1,2,4]

Solution 2 - Arrays

All great answers! Here's a more complete Swift 5 solution with performance in mind and bonus for benchmark and GIF fans. ✌️

extension Array where Element: Equatable
{
    mutating func move(_ element: Element, to newIndex: Index) {
        if let oldIndex: Int = self.firstIndex(of: element) { self.move(from: oldIndex, to: newIndex) }
    }
}

extension Array
{
    mutating func move(from oldIndex: Index, to newIndex: Index) {
        // Don't work for free and use swap when indices are next to each other - this
        // won't rebuild array and will be super efficient.
        if oldIndex == newIndex { return }
        if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
        self.insert(self.remove(at: oldIndex), at: newIndex)
    }
}

GIF

Solution 3 - Arrays

edit/update: Swift 3.x

extension RangeReplaceableCollection where Indices: Equatable {
    mutating func rearrange(from: Index, to: Index) {
        precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indices")
        insert(remove(at: from), at: to)
    }
}

var numbers = [1,2,3,4]
numbers.rearrange(from: 1, to: 2)

print(numbers)  // [1, 3, 2, 4]

Solution 4 - Arrays

nice tip from Leo.

for Swift 3...5.5:

extension Array {  
	mutating func rearrange(from: Int, to: Int) {
		insert(remove(at: from), at: to)
	}
}

var myArray = [1,2,3,4]
myArray.rearrange(from: 1, to: 2)   
print(myArray)

Solution 5 - Arrays

var arr = ["one", "two", "three", "four", "five"]

// Swap elements at index: 2 and 3
print(arr)
arr.swapAt(2, 3)
print(arr)

Solution 6 - Arrays

Swift 4.2

extension Array where Element: Equatable {
    mutating func move(_ item: Element, to newIndex: Index) {
        if let index = index(of: item) {
            move(at: index, to: newIndex)
        }
    }
    
    mutating func bringToFront(item: Element) {
        move(item, to: 0)
    }
    
    mutating func sendToBack(item: Element) {
        move(item, to: endIndex-1)
    }
}

extension Array {
    mutating func move(at index: Index, to newIndex: Index) {
        insert(remove(at: index), at: newIndex)
    }
}

Solution 7 - Arrays

We can use swap method to swap items in an array :

var arr = ["one", "two", "three", "four", "five"]

// Swap elements at index: 2 and 3
print(arr)
swap(&arr[2], &arr[3])
print(arr)

Solution 8 - Arrays

@ian has provided good solution but it will be crash when array become out of bound added check for that too

extension Array where Element: Equatable {
    public mutating func move(_ element: Element, to newIndex: Index) {
        if let oldIndex: Int = index(of: element) {
            self.move(from: oldIndex, to: newIndex)
        }
    }

    public mutating func moveToFirst(item: Element) {
        self.move(item, to: 0)
    }

    public mutating func move(from oldIndex: Index, to newIndex: Index) {
        // won't rebuild array and will be super efficient.
        if oldIndex == newIndex { return }
        // Index out of bound handle here
        if newIndex >= self.count { return }
        // Don't work for free and use swap when indices are next to each other - this
        if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
        // Remove at old index and insert at new location
        self.insert(self.remove(at: oldIndex), at: newIndex)
    }
}

Solution 9 - Arrays

There is no move functionality in swift for arrays. you can take an object at an index by removing it from there and place it in your favourite index by using 'insert'

var swiftarray = [1,2,3,4]
let myobject = swiftarray.removeAtIndex(1) // 2 is the object at 1st index
let myindex = 3
swiftarray.insert(myobject, atIndex: myindex) // if you want to insert the    object to a particular index here it is 3
swiftarray.append(myobject) // if you want to move the object to last index

Solution 10 - Arrays

Swift 4 - Solution for moving a group of items from an IndexSet of indices, grouping them and moving them to a destination index. Realised through an extension to RangeReplaceableCollection. Includes a method to remove and return all items in an IndexSet. I wasn't sure how to constrain the extension to a more generalised form than to constrain the element than integer while maintaining the ability to construct IndexSets as my knowledge of Swift Protocols is not that extensive.

extension RangeReplaceableCollection where Self.Indices.Element == Int {

    /**
	    Removes the items contained in an `IndexSet` from the collection.
	    Items outside of the collection range will be ignored.

	    - Parameter indexSet: The set of indices to be removed.
    	- Returns: Returns the removed items as an `Array<Self.Element>`.
    */
    @discardableResult
    mutating func removeItems(in indexSet: IndexSet) -> [Self.Element] {
	
    	var returnItems = [Self.Element]()
	
	    for (index, _) in self.enumerated().reversed() {
		    if indexSet.contains(index) {
			    returnItems.insert(self.remove(at: index), at: startIndex)
		    }
	    }
    	return returnItems
    }


    /**
	    Moves a set of items with indices contained in an `IndexSet` to a     
        destination index within the collection.

	    - Parameters:
		    - indexSet: The `IndexSet` of items to move.
		    - destinationIndex: The destination index to which to move the items.
	    - Returns: `true` if the operation completes successfully else `false`.

	    If any items fall outside of the range of the collection this function 
        will fail with a fatal error.
    */
    @discardableResult
    mutating func moveItems(from indexSet: IndexSet, to destinationIndex: Index) -> Bool {
	
	    guard indexSet.isSubset(of: IndexSet(indices)) else {
		    debugPrint("Source indices out of range.")
		    return false
		    }
	    guard (0..<self.count + indexSet.count).contains(destinationIndex) else {
		    debugPrint("Destination index out of range.")
		    return false
	    }
	
	    let itemsToMove = self.removeItems(in: indexSet)
	
	    let modifiedDestinationIndex:Int = {
		    return destinationIndex - indexSet.filter { destinationIndex > $0 }.count
	    }()
	
	    self.insert(contentsOf: itemsToMove, at: modifiedDestinationIndex)
	
	    return true
    }
}

Solution 11 - Arrays

Here's a solution with functions to both change the array in-place and to return a changed array:

extension Array {
	func rearranged(from fromIndex: Int, to toIndex: Int) -> [Element] {
		var arr = self
		let element = arr.remove(at: fromIndex)
		
		if toIndex >= self.count {
			arr.append(element)
		} else {
			arr.insert(element, at: toIndex)
		}
		return arr
	}
	
	mutating func rearrange(from fromIndex: Int, to toIndex: Int) {
		let element = self.remove(at: fromIndex)
		if toIndex >= self.count {
			self.append(element)
		} else {
			self.insert(element, at: toIndex)
		}
	}
}

Solution 12 - Arrays

>Update with Swift 4, Swipe array index

for (index,addres) in self.address.enumerated() {
     if addres.defaultShipping == true{
          let defaultShipping = self.address.remove(at: index)
          self.address.insert(defaultShipping, at: 0)
     }
}

Solution 13 - Arrays

Efficient solution:

extension Array 
{
    mutating func move(from sourceIndex: Int, to destinationIndex: Int)
    {
        guard
            sourceIndex != destinationIndex
            && Swift.min(sourceIndex, destinationIndex) >= 0
            && Swift.max(sourceIndex, destinationIndex) < count
        else {
            return
        }
    
        let direction = sourceIndex < destinationIndex ? 1 : -1
        var sourceIndex = sourceIndex
    
        repeat {
            let nextSourceIndex = sourceIndex + direction
            swapAt(sourceIndex, nextSourceIndex)
            sourceIndex = nextSourceIndex
        }
        while sourceIndex != destinationIndex
    }
}

Solution 14 - Arrays

func adjustIndex(_ index: Int, forRemovalAt removed: Int) -> Int {
    return index <= removed ? index : index - 1
}

extension Array
{
    mutating func move(from oldIndex: Index, to newIndex: Index) {
        insert(remove(at: oldIndex), at: adjustIndex(newIndex, forRemovalAt: oldIndex))
    }
}

Solution 15 - Arrays

Leo Dabus's solution is great however using precondition(from != to && indices.contains(from != to && indices.contains(to), "invalid indexes"), will crash the app if the conditions are not met. I changed it to guard and an if statement - if for some reason the conditions are not met, nothing happens and the app continues. I think we should avoid making extensions that may crash the app. If you wish you could make the rearrange function return a Bool - true if successful and false if failed. The safer solution:

extension Array {
mutating func rearrange(from: Int, to: Int) {
    guard from != to else { return }
    //precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indexes")
    if indices.contains(from) && indices.contains(to) {
        insert(remove(at: from), at: to)
    }
}

Solution 16 - Arrays

Function(not swift but universal.. lookup/remove/insert):

func c_move_to(var array:Array,var from:Int,var to:Int):

	var val = array[from]
	array.remove(from)
	array.insert(to,val)
	return array

How to use:

print("MOVE 0 to 3  [1,2,3,4,5]"  , c_move_to([1,2,3,4,5],0,3))
print("MOVE 1 to 2  [1,2,3,4,5]"  , c_move_to([1,2,3,4,5],1,2))	

spits out:

MOVE 0 to 3  [1,2,3,4,5][2, 3, 4, 1, 5]
MOVE 1 to 2  [1,2,3,4,5][1, 3, 2, 4, 5]

Solution 17 - Arrays

How about this solution? The element to be changed and the element to be changed have been changed.

// Extenstion

extension Array where Element: Equatable {
  mutating func change(_ element: Element, to newIndex: Index) {
    if let firstIndex = self.firstIndex(of: element) {
      self.insert(element, at: 0)
      self.remove(at: firstIndex + 1)
    }
  }
}

// Example

var testArray = ["a", "b", "c", "EE", "d"]
testArray.change("EE", to: 0)

// --> Result
// ["EE", "a", "b", "c", "d"]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMorty ChoiView Question on Stackoverflow
Solution 1 - ArraysLuka JacobowitzView Answer on Stackoverflow
Solution 2 - ArraysIan BytchekView Answer on Stackoverflow
Solution 3 - ArraysLeo DabusView Answer on Stackoverflow
Solution 4 - ArraysingcontiView Answer on Stackoverflow
Solution 5 - ArraysVinoth AnandanView Answer on Stackoverflow
Solution 6 - ArraysZelkoView Answer on Stackoverflow
Solution 7 - ArraysMuhammad Yawar AliView Answer on Stackoverflow
Solution 8 - ArraysAjay Singh MehraView Answer on Stackoverflow
Solution 9 - Arraysuser4615157View Answer on Stackoverflow
Solution 10 - ArraysCodeCoordinationView Answer on Stackoverflow
Solution 11 - ArraysCharlie WelshView Answer on Stackoverflow
Solution 12 - ArraysRaj JoshiView Answer on Stackoverflow
Solution 13 - ArraysEnlodView Answer on Stackoverflow
Solution 14 - ArraysChris HarperView Answer on Stackoverflow
Solution 15 - ArraysJonathan HocheView Answer on Stackoverflow
Solution 16 - ArraysChristopher SchieferView Answer on Stackoverflow
Solution 17 - ArraysdaveView Answer on Stackoverflow