2016-03-04 3 views
0

Comme un exercice d'apprentissage, je suis en train de utiliser un pour remplacer un String dans un Array (avec une valeur dictionnaire) pour en boucle si le String est un clé existante dans un Dictionary. Je sais déjà comment faire cela en utilisant .stringByReplacingOccurrencesOfString, mais j'aimerais savoir ce que je fais de mal ici, et comment y parvenir avec une boucle for-in.Swift chaîne de remplacement dans le tableau à l'aide pour en boucle

let sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 
var toArray = sillyMonkeyString.componentsSeparatedByString(" ") 
// desired result is "A stole my " 

Voici ce qui ne fonctionne pas:

for var str in toArray { 
    if let val = dictionary[str] { 
     str = val     
    } 
} 
let backToString = toArray.joinWithSeparator(" ") 

Qu'est-ce que le travail:

var newArray = [String]() 
for var str in toArray { 
    if let val = dictionary[str] { 
     str = val 
    } 
    newArray.append(str) 
} 
let backToString = newArray.joinWithSeparator(" ") 

Cela fonctionne parce que je crée un nouveau tableau et annexant au nouveau tableau. Cependant, mon Array d'origine est mutable, alors pourquoi la première solution n'attribue-t-elle pas correctement str à val dans le Array d'origine à l'intérieur de la boucle for-in? La seule autre question connexe que j'ai trouvé here a eu une excellente réponse d'une ligne qui n'a pas expliqué si oui ou non je peux accomplir cela en utilisant une boucle for-in.

MISE À JOUR: Je ne recommande pas l'implémentation d'une boucle for-in pour ce cas d'utilisation particulier. J'ai demandé à cette question d'apprendre comment pour ce faire. Si un utilisateur souhaite remplacer des parties de chaînes par un dictionnaire, je recommande fortement d'utiliser l'une des solutions les plus efficaces et les plus rapides ci-dessous (ce qui n'est peut-être pas la solution acceptée)

+3

Puisque vous essayez d'apprendre par vous-même - voici un défi bonus: Plutôt que d'utiliser 'for-in', utilisez la fonction' reduce' sur le tableau. – Abizern

+0

J'ai fait le cours sur udacity ainsi que – jbcd13

+0

vous pouvez utiliser pour in loop (voir ma réponse), mais je vous recommande plus de solution «fonctionnelle» et swifty – user3441734

Répondre

1

Lorsque vous déclarez quelque chose comme "var" dans un Pour la boucle ou l'instruction "if var" (obsolète), la valeur est copiée dans cette variable et non dans la référence. Alors, quand vous changez « val », vous changez cette variable locale et ne pas changer l'objet qui existe dans toArray

Si vous voulez changer dans le tableau, cela devrait fonctionner (non compilé FYI):

for str in toArray { 
    if let val = dictionary[str] { 
     toArray[toArray.indexOf(str)] = val 
    } 
} 

Alternativement, vous pouvez suivre l'indice à la place avec

for i in 0 ..< toArray.count { 
    if let val = dictionary[toArray[i]] { 
     toArray[i] = val 
    } 
} 

pour donner un exemple comparable au lien que vous avez envoyé, vous pouvez faire quelque chose comme ceci:

toArray = toArray.map { dictionary[$0] ?? $0 } 

Si vous n'êtes pas familier avec la fonction de carte, je suggérerais de la rechercher car c'est l'une des meilleures fonctionnalités de swift! :)

+0

Je comprends la cartographie. Je suis curieux de savoir pourquoi le défi d'exercice sur lequel je travaille me demande explicitement de le résoudre avec une boucle for-in. Ma solution me semble délicate, mais si le défi est de demander un for-in, je suppose qu'il y a une solution élégante à découvrir. Je ne peux pas encore obtenir votre première solution. Je pense qu'il devrait y avoir un autre moyen que d'utiliser des index. poussant sur .. (et merci pour l'aide =)) – jungledev

+0

Vu une erreur potentielle. Je crois qu'il devrait être .indexOf (str) (réponse mise à jour) – PeejWeej

+0

Très proche. Je l'ai juste compris. Votre solution a émis une erreur facultative. J'ai ajouté le mien. – jungledev

1

str = val ne modifiera que la valeur de votre variable locale, qui n'est valide que dans le cadre de la boucle for. La valeur dans le tableau ne sera pas modifiée.Ce que vous voulez est vraiment:

for i in 0..< toArray.count { 
    if let val = dictionary[toArray[i]] { 
     toArray[i] = val 
    } 
} 
+1

FYI cette boucle de style C traditionnelle est dépréciée dans Swift 2.2 et sera supprimée dans Swift 3.0 – PeejWeej

+0

@PEEJWEEJ Merci. Mis à jour à la nouvelle syntaxe swift. – tanzolone

+0

Vous commencez à boucler à l'index 1, cela provoquera l'erreur indexOutOfBounds. Cela devrait être "for i in 0 .. PeejWeej

0

Comme d'autres l'ont dit, au lieu de

for var str in toArray { 

si doit être

for str in toArray { 

Mais je veux montrer une autre approche, en utilisant énumérer() sur dictionnaire

var sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 

for (_ ,v) in dictionary.enumerate() { 
    sillyMonkeyString = sillyMonkeyString.stringByReplacingOccurrencesOfString(v.0, withString: v.1) 
} 

print(sillyMonkeyString) 

estampes A stole my

enumerate() renvoie un tuple contenant l'index et le tuple avec la clé et la valeur.

et de faire tanzolone heureux, bien que ce soit une approche assez stupide:

let sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 
var toArray = sillyMonkeyString.componentsSeparatedByString(" ") 

for (i,v) in dictionary.enumerate() { 
    for (j,s) in toArray.enumerate() { 
     if s == v.0 { 
      toArray[j] = v.1 
     } 
    } 
} 

print(toArray.joinWithSeparator(" ")) 

énumération avec dans

var sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 

for (k,v) in dictionary{ 
    sillyMonkeyString = sillyMonkeyString.stringByReplacingOccurrencesOfString(k, withString: v) 
} 

print(sillyMonkeyString) 
+0

PO a clairement dit: "Je sais déjà comment faire cela en utilisant .stringByReplacingOccurrencesOfString, mais je voudrais savoir ce que je fais mal ici et comment y parvenir avec une boucle for-in. " – tanzolone

+1

l'accent principal ici est que j'utilise l'énumération appropriée comme demandé par OP (pour). Votre réponse viole cet aspect principal de la question. – vikingosegundo

+0

Donc, .enumerate est vraiment moins cher car il convertit le tableau en dictionnaire et n'énumère qu'une fois? Profitant vraiment de ce dialogue =) – jungledev

1

C'est la solution que je suis venu avec. Je vais envisager d'utiliser d'autres, car beaucoup ont recommandé que l'utilisation de .indexOf est très coûteux. Plutôt que d'assigner à un ivar, j'affecte val directement à l'article à l'index courant dans le tableau original. Je suis tout à fait pour apprendre et mettre en place ce qui coûte le moins cher. Cependant, vous devez admettre que c'est propre et court. (Mais pas le plus efficace)

for var str in toArray { 
    if let val = dictionary[str] { 
     toArray[toArray.indexOf(str)!] = val 
    } 
} 
+1

juste un indice: indexOf est potentiellement cher (il énumère chaque fois depuis le début) et peut échouer si des objets égaux sont dans un tableau (il retourne le premier index d'un objet égal). – vikingosegundo

+1

J'évite de l'utiliser en utilisant des méthodes d'énumération ou en gérant ma propre variable d'index. – vikingosegundo

+0

Veuillez expliquer comment ce code répond à la question. – JAL

1

Comment cette approche:

var sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 
var toArray = sillyMonkeyString.componentsSeparatedByString(" ") 

for (index, string) in toArray.enumerate() 
{ 
    if let val = dictionary[string] 
    { 
    toArray[index] = val 
    } 
} 
let finalString = toArray.joinWithSeparator(" ") 

Ce code utilise une variante de pour ... qui retourne les objets dans un tableau et l'index comme tuples. D'autres réponses ont utilisé indexOf. indexOf est à la fois coûteux en termes de calcul et potentiellement risqué. Les tableaux peuvent contenir plusieurs copies du même objet, de sorte que la chaîne en question peut apparaître à plusieurs endroits. Dans cet exemple particulier, cela fonctionne parce que nous parcourons le tableau de mots avant et arrière, en remplaçant les mots correspondants au fur et à mesure. Ainsi, indexOf trouvera toujours la première occurrence restante de la chaîne. Néanmoins, il est encore assez lent sur les grandes baies.

2

'pure' Swift (pas Fondation d'importation) et le mode 'fonctionnelle' conduit à

let sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 

let arr = sillyMonkeyString.characters.split(" ").map(String.init) 
let result = arr.map { dictionary[$0] ?? $0 }.joinWithSeparator(" ") 

print(result) 
// A stole my 

si dans la boucle est nécessaire, que

var resArr:[String] = [] 
for txt in arr { 
    resArr.append(dictionary[txt] ?? txt) 
} 
let result2 = resArr.joinWithSeparator(" ") // A stole my " 

si vous souhaitez avoir solution de mutation ...

let sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 

var arr = sillyMonkeyString.characters.split(" ").map(String.init) 

for (idx,value) in arr.enumerate() { 
    arr[idx] = dictionary[value] ?? value 
} 
let result3 = arr.joinWithSeparator(" ") // "A stole my " 

si vous ne l'aimez pas enumerate(), vous pouvez utiliser des indices au lieu

let sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 

var arr = sillyMonkeyString.characters.split(" ").map(String.init) 

for i in arr.indices { 
    arr[i] = dictionary[arr[i]] ?? arr[i] 
} 
let result4 = arr.joinWithSeparator(" ") // "A stole my " 
+0

Votre 2ème réponse est ce que j'ai posté dans ma question à l'origine, car je savais que cela fonctionnait si je créais un nouveau tableau, mais je voulais savoir comment éviter de le faire. – jungledev

+0

@jungledev OK, j'ajoute la solution 'mutation' :-) – user3441734

1

Vous pouvez utiliser foreach pour itérer vos paires clé/valeur dictionnaire et les remplacer comme suit:

var sillyMonkeyString = "A monkey stole my iPhone" 
let dictionary = ["monkey": "", "iPhone":""] 

dictionary.forEach{ sillyMonkeyString = sillyMonkeyString.stringByReplacingOccurrencesOfString($0.0, withString: $0.1) } 
print(sillyMonkeyString) // "A stole my " 
1

sur le thème de « ce qui était faux » dans le code qui ne fonctionne pas:

for var str in toArray { 
    if let val = dictionary[str] { 
     str = val     
    } 
} 
let backToString = toArray.joinWithSeparator(" ") 

La variable str dans votre boucle for n'est pas une référence aux éléments de votre toArray mais une copie de celle-ci. La modification de str dans la boucle n'a donc aucun effet sur le tableau d'origine.