Comment faire une copie exacte d'un tableau?

100

Comment faire une copie exacte d'un tableau?

J'ai du mal à trouver des informations sur la duplication d'un tableau dans Swift.

J'ai essayé d'utiliser .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Patrick
la source
5
pourquoi n'attribuez-vous pas une valeur directement comme ceci:var duplicateArray = originalArray
Dharmesh Kheni
1
Cela ne fonctionne pas dans mon cas. Cela crée un autre objet qui n'est qu'une référence au même tableau et vous vous retrouvez avec 2 variables référençant le même tableau.
user1060500

Réponses:

176

Les tableaux ont une sémantique pleine valeur dans Swift, il n'y a donc pas besoin de quelque chose d'extraordinaire.

var duplicateArray = originalArray est tout ce dont vous avez besoin.


Si le contenu de votre tableau est un type de référence, alors oui, cela ne copiera que les pointeurs vers vos objets. Pour effectuer une copie complète du contenu, vous utiliseriez mapet exécuteriez plutôt une copie de chaque instance. Pour les classes Foundation conformes au NSCopyingprotocole, vous pouvez utiliser la copy()méthode:

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

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

Notez qu'il y a des pièges contre lesquels la sémantique des valeurs de Swift travaille pour vous protéger - par exemple, étant donné que NSArrayreprésente un tableau immuable, sa copyméthode renvoie simplement une référence à elle-même, de sorte que le test ci-dessus donnerait des résultats inattendus.

Nate Cook
la source
J'ai essayé cela dans la cour de récréation avec ce code simple var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }et j'ai obtenu cette sortie: 0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ne semble pas être copié, savez-vous pourquoi?
Phil Niedertscheider
@PNGamingPower: x contient des adresses. y contient des copies de ces adresses. Si vous modifiez x [0], y [0] ne changera pas. (essayez x [0] = x [1], y [0] ne changera pas). Ainsi, y est une copie complète de x, mais vous n'avez copié que les pointeurs, pas ce qu'ils pointent.
ragnarius
@ragnarius donc fondamentalement, nous devons définir ce que signifie "copier", soit en copiant le pointeur, soit en copiant la valeur. C'est donc la solution pour copier / dupliquer le tableau de pointeurs, mais comment dupliquer le tableau de valeurs? Le but serait x[0] == x[1]mais x[0] === y[0]devrait échouer
Phil Niedertscheider
Cela devrait être la réponse acceptée, car la sémantique des valeurs de Array rend une "copie" du tableau inutile.
Scott Ahten
Cela ne fonctionne pas pour moi. Si je suis cette méthode, j'obtiens deux références qui finissent par pointer vers le même tableau d'objets. Si je supprime un élément de la liste, il est reflété dans les deux références d'objet car la liste n'a pas été copiée, mais l'objet a simplement été référencé.
user1060500
28

Nate a raison. Si vous travaillez avec des tableaux primitifs, tout ce que vous avez à faire est d'assigner duplicateArray à l'originalArray.

Par souci d'exhaustivité, si vous travailliez sur un objet NSArray, procédez comme suit pour faire une copie complète d'un NSArray:

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

var duplicateArray = NSArray(array:originalArray, copyItems: true)
applejack42
la source
C'est bien! Je vous remercie!
Patrick
23

Il y a une troisième option à la réponse de Nate:

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

* EDITED * La modification commence ici

Ci-dessus est essentiellement le même que ci-dessous et l'utilisation de l'opérateur d'égalité ci-dessous fonctionnera mieux car le tableau ne sera pas copié à moins qu'il ne soit modifié (c'est par conception).

let z = x

En savoir plus ici: https://developer.apple.com/swift/blog/?id=10

* EDITED * la modification se termine ici

l'ajout ou la suppression de ce tableau n'affectera pas le tableau d'origine. Cependant, la modification des propriétés des objets contenues dans le tableau serait visible dans le tableau d'origine. Parce que les objets du tableau ne sont pas des copies (en supposant que le tableau contient des objets, pas des nombres primitifs).

oyalhi
la source
cela fait effet, je l'ai testé. il y a deux tableaux, si vous changez en 1, le second est effectué
Filthy Knight
1
Non, ce n'est pas le cas, à moins que le tableau ne contienne des types primitifs au lieu d'objets. Ensuite, cela affecte comme indiqué dans la réponse. Un cas de test 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
Veuillez voir l'essentiel que j'ai créé pour un meilleur exemple en utilisant une classe personnalisée: gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi
Si votre classe prend en charge NSCopying, alors dupliquez un tableau:let z = x.map { $0.copy as! ClassX }
John Pang
Si vous utilisez les BufferPointers de Swift, c'est la version que vous devez utiliser comme copie directe. Avant de modifier une valeur dans l'original ou dans le tableau copié, Swift copiera les valeurs de l'original dans la copie, puis continuera. Si vous utilisez plutôt des pointeurs, Swift ne le fera pas maintenant si ou quand des changements ont lieu, vous pourriez donc finir par changer les deux tableaux.
Justin Ganzer le
16

Pour les objets normaux, ce qui peut être fait est d'implémenter un protocole prenant en charge la copie et de faire en sorte que la classe d'objets implémente ce protocole comme ceci:

protocol Copying {
    init(original: Self)
}

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

Et puis l'extension Array pour le clonage:

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

et c'est à peu près tout, pour afficher le code et un échantillon, vérifiez ce point essentiel

Sohayb Hassoun
la source
Cela a permis de gagner beaucoup de temps, merci.
Abhijit
Pour les sous-classes, le protocole ne peut pas garantir que l'exigence init est implémentée avec le type de la sous-classe. Vous déclarez un protocole de copie qui implémente copy pour vous, mais vous implémentez toujours clone (), cela n'a pas de sens.
Binarian
1
La copie @iGodric concerne les éléments de la collection et le clone concerne l'ensemble de la collection, disponible lorsque la copie est implémentée pour ses éléments. Ça a du sens? En outre, le compilateur garantit que les sous-classes suivent le protocole requis par leur parent.
johnbakers
@johnbakers Oh ouais, maintenant je le vois. Merci pour l'explication.
Binarian
Implémentation très propre et évite l'agitation inutile de passer des paramètres dans la object'sfonction init
Sylvan D Ash
0

Si vous souhaitez copier les éléments d'un tableau d'un objet de classe. Ensuite, vous pouvez suivre le code ci-dessous sans utiliser le protocole NSCopying mais vous devez avoir une méthode init qui devrait prendre tous les paramètres requis pour votre objet. Voici le code d'un exemple à tester sur terrain de jeu.

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
la source