123

How would I make an exact duplicate of an array?

I am having hard time finding information about duplicating an array in Swift.

I tried using .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
Patrick
  • 1,231
  • 2
  • 8
  • 3
  • 5
    why dont you assign value directly like this: `var duplicateArray = originalArray` – Dharmesh Kheni Jan 07 '15 at 04:37
  • 2
    That doesn't work in my case. That creates another object that is just a reference to the same array and you end up with 2 variables referencing the same array. – user1060500 Sep 02 '19 at 22:00

5 Answers5

210

Arrays have full value semantics in Swift, so there's no need for anything fancy.

var duplicateArray = originalArray is all you need.


If the contents of your array are a reference type, then yes, this will only copy the pointers to your objects. To perform a deep copy of the contents, you would instead use map and perform a copy of each instance. For Foundation classes that conform to the NSCopying protocol, you can use the copy() method:

let x = [NSMutableArray(), NSMutableArray(), NSMutableArray()]
let y = x
let z = x.map { $0.copy() }

x[0] === y[0]   // true
x[0] === z[0]   // false

Note that there are pitfalls here that Swift's value semantics are working to protect you from—for example, since NSArray represents an immutable array, its copy method just returns a reference to itself, so the test above would yield unexpected results.

Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • I tried this in playground with this simple code `var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }` and I got this output: `0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770` Doesn't look like it is being copied, do you know why? – Phil Niedertscheider Aug 07 '15 at 12:32
  • @PNGamingPower: x contains addresses. y contains copies of those addresses. If you alter x[0], y[0] will not change. (try x[0] = x[1], y[0] will not change). Thus, y is a deep copy of x.But you have only copied the pointers, not what they are pointing to. – ragnarius Aug 09 '15 at 20:23
  • @ragnarius so basically we have to define what "copy" means, either copying the pointer or the value. Therefore this is the solution for copying/duplicating the array of pointers, but how do you duplicate the array of values? The goal would be `x[0] == x[1]` but `x[0] === y[0]` should fail – Phil Niedertscheider Aug 09 '15 at 20:28
  • This should be the accepted answer, as value semantics of Array makes a "copy" of the array unnecessary. – Scott Ahten Aug 17 '18 at 16:39
  • This doesn't work for me. If I follow that method, I get two references that end up pointing to the same array of objects. If I remove an item from the list it's reflected in both object references since the list wasn't copied, but rather the object was just referenced. – user1060500 Sep 02 '19 at 22:02
35

There is a third option to Nate's answer:

let z = x.map { $0 }  // different array with same objects

* EDITED * edit starts here

Above is essentially the same as below and actually using the equality operator below will perform better since the array won't be copied unless it is changed (this is by design).

let z = x

Read more here: https://developer.apple.com/swift/blog/?id=10

* EDITED * edit ends here

adding or removing to this array won't affect the original array. However, changing any of the objects' any properties that the array holds would be seen in the original array. Because the objects in the array are not copies (assuming the array hold objects, not primitive numbers).

oyalhi
  • 3,834
  • 4
  • 40
  • 45
  • 1
    it does effect, i have tested it. there are two arrays, if u change in 1, the second is effected – IDev Apr 26 '17 at 08:13
  • 1
    Nope it doesn't, unless array holds primitive types instead of objects. Then it does affect as stated in the answer. A simple test case: `var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);` – oyalhi Apr 26 '17 at 08:20
  • 2
    Please see this gist I created for better example using a custom class: https://gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce – oyalhi Apr 26 '17 at 08:39
  • If your class support `NSCopying`, then duplicate an array: `let z = x.map { $0.copy as! ClassX }` – John Pang Sep 05 '18 at 17:05
  • If using Swift's BufferPointers, this is the version you should use as its a direct copy. Before changing a value in either the original or the copied array, Swift will copy the values of the original to the copy and then continue. If you use Pointers instead, Swift will not now if or when changes take place, thus you could potentially end up changing both arrays. – Justin Ganzer Sep 04 '19 at 10:40
33

Nate is correct. If you are working with primitive arrays all you need to do is assign duplicateArray to the originalArray.

For the sake of completeness, if you were working an NSArray object, you would do the following to do a full copy of an NSArray:

var originalArray = [1, 2, 3, 4] as NSArray

var duplicateArray = NSArray(array:originalArray, copyItems: true)
applejack42
  • 1,155
  • 9
  • 18
18

For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:

protocol Copying {
    init(original: Self)
}

extension Copying {
    func copy() -> Self {
        return Self.init(original: self)
    }
}

And then the Array extension for cloning:

extension Array where Element: Copying {
    func clone() -> Array {
        var copiedArray = Array<Element>()
        for element in self {
            copiedArray.append(element.copy())
        }
        return copiedArray
    }
}

and that is pretty much it, to view code and a sample check this gist

Sohayb Hassoun
  • 657
  • 7
  • 17
  • This saved lot of time, Thank you. – Abhijit Mar 08 '16 at 05:51
  • For subclasses the protocol can not guarantee that the requirement init is implemented with the subclass's type. You are declaring a copying protocol which implements copy for you, but you still are implementing clone(), that does not make sense. – Binarian Jun 21 '16 at 13:42
  • 1
    @iGodric copy is for elements in the collection, and clone is for the entire collection, available when copy is implemented for its elements. Make sense? Also, the compiler ensures that subclasses do follow the protocol their parent requires. – johnbakers Jun 16 '17 at 20:27
  • @johnbakers Oh yeah, now I see it. Thanks for explanation. – Binarian Jun 17 '17 at 05:36
  • Very clean implementation and avoids the unnecessary hustle of passing in any parameters in the `object's` init function – Sylvan D Ash Apr 12 '18 at 09:37
  • How would you modify it to be used with an array of elements where every element also contains an array? – Recusiwe Oct 14 '18 at 00:38
0

If you want to copy the items of an array of some class object. Then you can follow the below code without using NSCopying protocol but you need to have an init method which should take all the parameters that are required for your object. Here is the code for an example to test on playground.

class ABC {
    
    var a = 0
    func myCopy() -> ABC {
        
        return ABC(value: self.a)
    }
    
    init(value: Int) {
        
        self.a = value
    }
}

var arrayA: [ABC] = [ABC(value: 1)]
var arrayB: [ABC] = arrayA.map { $0.myCopy() }

arrayB.first?.a = 2
print(arrayA.first?.a)//Prints 1
print(arrayB.first?.a)//Prints 2
Noman Haroon
  • 185
  • 1
  • 11