Random replace using Swift

  • A+

I am experiencing a problem that I am not sure how to solve and I hope someone here can help me. Currently I have a string variable and later I replace the letters in the string with underscores like the following:

var str = "Hello playground"  let replace = str.replacingOccurrences(of: "//S", with: "_", options: .regularExpression)  print(str) 

Know I would like to randomly generate 25 % of the characters in str (In this case 16 * 0,25 = 4) so it later prints something like these examples:

str = "H__l_ ___yg_____"  str = "_____ play______"  str = "__ll_ ____g____d" 

Does anyone have any ideas of how to do this?


A possible solution:

var str = "Hello playground" print("Before: /(str)") do {     let regex = try NSRegularExpression(pattern: "//S", options: [])     let matches = regex.matches(in: str, options: [], range: NSRange(location: 0, length: str.utf16.count))      //Retrieve 1/4 elements of the string     let randomElementsToReplace = matches.shuffled().dropLast(matches.count * 1/4)      matches.forEach({ (aMatch) in         if randomElementsToReplace.first(where: { $0.range == aMatch.range } ) != nil {             str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_")         } else {             //Do nothing because that's the one we are keeping as such         }     })     print("After: /(str)") } catch {     print("Error while creating regex: /(error)") } 

The idea behind it: Use the same Regular Expression pattern as the one you used.
Pick up n elements in it (in your case 1/4)
Replace every character that isn't in that short list.

Now that you got the idea, it's even faster replacing the for loop with

for aMatch in randomElementsToReplace {     str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_") } 

Thanks to @Martin R's comment for pointing it out.

Output (done 10 times):

$>Before: Hello playground $>After: ____o ___y____n_ $>Before: Hello playground $>After: _el__ _______u__ $>Before: Hello playground $>After: _e___ ____g___n_ $>Before: Hello playground $>After: H___o __a_______ $>Before: Hello playground $>After: H___o _______u__ $>Before: Hello playground $>After: __l__ _____ro___ $>Before: Hello playground $>After: H____ p________d $>Before: Hello playground $>After: H_l__ _l________ $>Before: Hello playground $>After: _____ p____r__n_ $>Before: Hello playground $>After: H___o _____r____ $>Before: Hello playground $>After: __l__ ___y____n_ 

You'll see that there is a little difference from your expected result, it's because matches.count == 15, so 1/4 of them should be what? It's up to you there to do the correct calculation according to your needs (round up?, etc.) since you didn't specified it.

Note that if you don't want to round up, you could also do the reverse, use the randomed for the one to not replace, and then the round might play in your favor.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: