¿Cómo hago una copia duplicada exacta de una matriz?

100

¿Cómo haría un duplicado exacto de una matriz?

Me cuesta encontrar información sobre cómo duplicar una matriz en Swift.

Intenté usar .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Patricio
fuente
5
¿Por qué no asigna un valor directamente de esta manera:var duplicateArray = originalArray
Dharmesh Kheni
1
Eso no funciona en mi caso. Eso crea otro objeto que es solo una referencia a la misma matriz y termina con 2 variables que hacen referencia a la misma matriz.
user1060500

Respuestas:

176

Las matrices tienen semántica de valor completo en Swift, por lo que no hay necesidad de nada sofisticado.

var duplicateArray = originalArray es todo lo que necesitas.


Si el contenido de su matriz es un tipo de referencia, entonces sí, esto solo copiará los punteros a sus objetos. Para realizar una copia profunda de los contenidos, en su lugar, usaría mapy realizaría una copia de cada instancia. Para las clases Foundation que se ajustan al NSCopyingprotocolo, puede utilizar el copy()método:

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

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

Tenga en cuenta que aquí existen trampas de las que la semántica de valores de Swift está trabajando para protegerlo; por ejemplo, dado que NSArrayrepresenta una matriz inmutable, su copymétodo solo devuelve una referencia a sí mismo, por lo que la prueba anterior arrojaría resultados inesperados.

Nate Cook
fuente
Probé esto en el patio de recreo con este código simple var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }y obtuve este resultado: 0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 No parece que se esté copiando, ¿sabes por qué?
Phil Niedertscheider
@PNGamingPower: x contiene direcciones. y contiene copias de esas direcciones. Si modifica x [0], y [0] no cambiará. (intente x [0] = x [1], y [0] no cambiará). Por lo tanto, y es una copia profunda de x, pero solo ha copiado los punteros, no a lo que apuntan.
ragnarius
@ragnarius, así que básicamente tenemos que definir qué significa "copiar", ya sea copiando el puntero o el valor. Por lo tanto, esta es la solución para copiar / duplicar la matriz de punteros, pero ¿cómo se duplica la matriz de valores? El objetivo sería x[0] == x[1]pero x[0] === y[0]debería fallar
Phil Niedertscheider
Esta debería ser la respuesta aceptada, ya que la semántica de valores de Array hace que una "copia" de la matriz sea innecesaria.
Scott Ahten
Esto no me funciona. Si sigo ese método, obtengo dos referencias que terminan apuntando a la misma matriz de objetos. Si elimino un elemento de la lista, se refleja en ambas referencias de objeto, ya que la lista no se copió, sino que se hizo referencia al objeto.
user1060500
28

Nate tiene razón. Si está trabajando con matrices primitivas, todo lo que necesita hacer es asignar duplicateArray al originalArray.

En aras de la integridad, si estuviera trabajando con un objeto NSArray, haría lo siguiente para hacer una copia completa de un NSArray:

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

var duplicateArray = NSArray(array:originalArray, copyItems: true)
aguardiente de manzana, applejack42
fuente
¡Esto es genial! ¡Gracias!
Patrick
23

Hay una tercera opción para la respuesta de Nate:

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

* EDITADO * la edición comienza aquí

Arriba es esencialmente lo mismo que a continuación y, en realidad, usar el operador de igualdad a continuación funcionará mejor ya que la matriz no se copiará a menos que se cambie (esto es por diseño).

let z = x

Lea más aquí: https://developer.apple.com/swift/blog/?id=10

* EDITADO * La edición termina aquí

agregar o quitar a esta matriz no afectará la matriz original. Sin embargo, cambiar cualquiera de las propiedades de los objetos que contiene la matriz se vería en la matriz original. Porque los objetos en la matriz no son copias (asumiendo que la matriz contiene objetos, no números primitivos).

oyalhi
fuente
tiene efecto, lo he probado. hay dos matrices, si u cambia en 1, la segunda se efectúa
Filthy Knight
1
No, no lo hace, a menos que la matriz contenga tipos primitivos en lugar de objetos. Entonces afecta como se indica en la respuesta. Un caso de prueba simple:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
oyalhi
1
Por favor, vea esta esencia que creé para un mejor ejemplo usando una clase personalizada: gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi
Si su clase lo apoya NSCopying, entonces duplique una matriz:let z = x.map { $0.copy as! ClassX }
John Pang
Si usa BufferPointers de Swift, esta es la versión que debe usar como copia directa. Antes de cambiar un valor en la matriz original o copiada, Swift copiará los valores del original en la copia y luego continuará. Si usa punteros en su lugar, Swift no lo hará ahora si o cuando se produzcan cambios, por lo que podría terminar cambiando ambas matrices.
Justin Ganzer
16

Para los objetos normales, lo que se puede hacer es implementar un protocolo que admita la copia, y hacer que la clase de objeto implemente este protocolo de esta manera:

protocol Copying {
    init(original: Self)
}

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

Y luego la extensión Array para clonar:

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

y eso es todo, para ver el código y una muestra, consulte esta esencia

Sohayb Hassoun
fuente
Esto ahorró mucho tiempo, gracias.
Abhijit
Para las subclases, el protocolo no puede garantizar que el requisito init se implemente con el tipo de la subclase. Está declarando un protocolo de copia que implementa la copia para usted, pero aún está implementando clone (), eso no tiene sentido.
Binarian
1
La copia de @iGodric es para los elementos de la colección, y la copia es para toda la colección, disponible cuando se implementa la copia para sus elementos. ¿Tener sentido? Además, el compilador se asegura de que las subclases sigan el protocolo que requiere su padre.
johnbakers
@johnbakers Oh, sí, ahora lo veo. Gracias por la explicación.
Binarian
Implementación muy limpia y evita el ajetreo innecesario de pasar cualquier parámetro en la object'sfunción init
Sylvan D Ash
0

Si desea copiar los elementos de una matriz de algún objeto de clase. Luego, puede seguir el siguiente código sin usar el protocolo NSCopying, pero debe tener un método de inicio que debe tomar todos los parámetros que se requieren para su objeto. Aquí está el código de un ejemplo para probar en el patio de recreo.

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
Nadie Haroon
fuente