Shuffle characters in a string in Swift - swift

Can the new Swift 5 shuffle() method be used (directly, or in a more complicated incantation) to randomly shuffle the characters in a Swift string variable? (of length greater than 1)

You can simply try this nifty code
extension String {
func shuffleString(minLength : Int) -> String{
return self.count > minLength ? String(self.shuffled()) : self
}
}
var string = "Whatever is your string"
print(string.shuffleString(minLength: 1))

You can maybe try something like this.
var str = "Hello"
var shuffledString = String(str.shuffled())

try this function
func shuffleString(word: String) -> String {
var chars = Array(word.characters)
var result = ""
while chars.count > 0 {
let index = Int(arc4random_uniform(UInt32(chars.count - 1)))
chars[index].writeTo(&result)
chars.removeAtIndex(index)
}
return result
}

Try this one
var str = "Shuffle me please"
var shuffledStr: [Character]
if !str.isEmpty {
shuffledStr = str.shuffled()
print(String(shuffledStr))
}

Related

Cannot assign through subscript to Swift String

I have a class that contains a name, an image, a dashed form of the name, and the length of the name. For example, I could have "dog", an image of a dog, "---", and name length 3.
I just want to set name and pic for each object and have dashName and nameLength set automatically.
class Answer {
var name = "name"
var image: UIImage?
var dashName = "name"
var nameLength = 0
init(){
var a = 0
nameLength = name.characters.count
while a <= nameLength {
if (name[a] == " ") {dashName[a] = " "}
else {dashName[a] = "-"}
a += 1
}
}
}
The problem is the error that says: "cannot assign through subscript: subscript is get-only" and another error that says: "subscript is unavailable: cannot subscript String with an Int"
Because String's subscript operator is get-only, use map method instead, like:
class Answer {
var name = "name"
var image: UIImage?
var dashName = "name"
var nameLength = 0
init(){
dashName = String(name.map {$0 == " " ? " " : "-"})
}
}
As mentioned before,
Swift's String class is what other languages call a StringBuilder class, and for performance reasons, Swift does NOT provide setting character by index; If you don't care about performance a simple solution could be:
public static func replace(_ string: String, at index: Int, with value: String) {
let start = string.index(string.startIndex, offsetBy: index)
let end = string.index(start, offsetBy: 1)
string.replaceSubrange(start..<end, with: value)
}
Or as an extension:
extension String {
public func charAt(_ index: Int) -> Character {
return self[self.index(self.startIndex, offsetBy: index)];
}
public mutating func setCharAt(_ index: Int, _ new: Character) {
self.setCharAt(index, String(new))
}
public mutating func setCharAt(_ index: Int, _ new: String) {
let i = self.index(self.startIndex, offsetBy: index)
self.replaceSubrange(i...i, with: new)
}
}
Note how above needs to call index(...) method to convert integer to actual-index!? It seems, Swift implements String like a linked-list, where append(...) is really fast, but even finding the index (without doing anything with it) is a linear-time operation (and gets slower based on concatenation count).
The subscript operator for String is get-only, which means you can only read from a string using it, and have to use something else to write to a mutable String.
You can solve this issue, and clean up the code by using a map function on name
Swift 4
class Answer {
var name = "name"
var image: UIImage?
var dashName = "name"
var nameLength = 0
init()
{
nameLength = name.count
dashName = name.map { $0 == " " ? " " : "-" }.joined()
}
}
Swift 3
class Answer {
var name = "name"
var image: UIImage?
var dashName = "name"
var nameLength = 0
init()
{
nameLength = name.characters.count
dashName = name.characters.map { $0 == " " ? String(" ") : String("-") }.joined()
}
}

How to remove duplicate characters from a string in Swift

ruby has the function string.squeeze, but I can't seem to find a swift equivalent.
For example I want to turn bookkeeper -> bokepr
Is my only option to create a set of the characters and then pull the characters from the set back to a string?
Is there a better way to do this?
Edit/update: Swift 4.2 or later
You can use a set to filter your duplicated characters:
let str = "bookkeeper"
var set = Set<Character>()
let squeezed = str.filter{ set.insert($0).inserted }
print(squeezed) // "bokepr"
Or as an extension on RangeReplaceableCollection which will also extend String and Substrings as well:
extension RangeReplaceableCollection where Element: Hashable {
var squeezed: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
let str = "bookkeeper"
print(str.squeezed) // "bokepr"
print(str[...].squeezed) // "bokepr"
I would use this piece of code from another answer of mine, which removes all duplicates of a sequence (keeping only the first occurrence of each), while maintaining order.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var alreadyAdded = Set<Iterator.Element>()
return self.filter { alreadyAdded.insert($0).inserted }
}
}
I would then wrap it with some logic which turns a String into a sequence (by getting its characters), unqiue's it, and then restores that result back into a string:
extension String {
func uniqueCharacters() -> String {
return String(self.characters.unique())
}
}
print("bookkeeper".uniqueCharacters()) // => "bokepr"
Here is a solution I found online, however I don't think it is optimal.
func removeDuplicateLetters(_ s: String) -> String {
if s.characters.count == 0 {
return ""
}
let aNum = Int("a".unicodeScalars.filter{$0.isASCII}.map{$0.value}.first!)
let characters = Array(s.lowercased().characters)
var counts = [Int](repeatElement(0, count: 26))
var visited = [Bool](repeatElement(false, count: 26))
var stack = [Character]()
var i = 0
for character in characters {
if let num = asciiValueOfCharacter(character) {
counts[num - aNum] += 1
}
}
for character in characters {
if let num = asciiValueOfCharacter(character) {
i = num - aNum
counts[i] -= 1
if visited[i] {
continue
}
while !stack.isEmpty, let peekNum = asciiValueOfCharacter(stack.last!), num < peekNum && counts[peekNum - aNum] != 0 {
visited[peekNum - aNum] = false
stack.removeLast()
}
stack.append(character)
visited[i] = true
}
}
return String(stack)
}
func asciiValueOfCharacter(_ character: Character) -> Int? {
let value = String(character).unicodeScalars.filter{$0.isASCII}.first?.value ?? 0
return Int(value)
}
Here is one way to do this using reduce(),
let newChar = str.characters.reduce("") { partial, char in
guard let _ = partial.range(of: String(char)) else {
return partial.appending(String(char))
}
return partial
}
As suggested by Leo, here is a bit shorter version of the same approach,
let newChar = str.characters.reduce("") { $0.range(of: String($1)) == nil ? $0.appending(String($1)) : $0 }
Just Another solution
let str = "Bookeeper"
let newChar = str.reduce("" , {
if $0.contains($1) {
return "\($0)"
} else {
return "\($0)\($1)"
}
})
print(str.replacingOccurrences(of: " ", with: ""))
Use filter and contains to remove duplicate values
let str = "bookkeeper"
let result = str.filter{!result.contains($0)}
print(result) //bokepr

How to make a function that swaps 3 characters in a string?

For example, if I had a string RedSox and wanted to change it to SoxRed?
I'm thinking it would be something like :
func swapString (String: String) -> String {
var stringReplaced = String
var result = stringReplaced.Select(x=> x == 'A' ? 'B' : (x=='B' ? "A" : x)).ToArray()
stringReplaced = String(result)
return stringReplaced
}
this function takes the last 3 characters of a string and appends them to the beginning, there are definitely less verbose ways of doing this but it works. it will throw an error if passed a string with < 3 characters.
import UIKit
let string = "RedSox"
func changeString ( _ string : String) -> String {
var characters : Array<Character> = []
for character in string.characters {
characters.append(character)
}
var characters2 : Array<Character> = []
var position = characters.count - 3
while position < characters.count {
characters2.append(characters[position])
position += 1
}
characters.removeLast()
characters.removeLast()
characters.removeLast()
characters2.append(contentsOf: characters)
return (String(characters2))
}
var newString = changeString(string)
print (newString)
Just use the methods which the String class already provides.
It's always a good idea putting these kind of "helper" methods in an extension.
// Define String extension
extension String {
func swappedString(count swapCount: Int) -> String {
guard self.characters.count > swapCount else {
return self
}
let index = self.index(self.endIndex, offsetBy: -swapCount)
let first = self.substring(from: index)
let second = self.substring(to: index)
return first + second
}
}
// Use it
"RedSox".swappedString(count: 3) //= SoxRed

Reversing the order of a string value

I have the following function which reverses a string value's display order.
I'm new to Swift and I'm trying to understand it's logic. What is going on with the '!pleh' value that it turns into 'Help!' ?
Thanks
func reverse(_ s: String) -> String {
var str = ""
for character in s.characters {
str = "\(character)" + str
}
return str
}
print (reverse("!pleH"))
In swift 4.0, directly call reversed on a string will get the job done
let str = "abc"
String(str.reversed()) // This will give you cba
Commented inline,
func reverse(_ s: String) -> String {
var str = ""
//.characters gives the character view of the string passed. You can think of it as array of characters.
for character in s.characters {
str = "\(character)" + str
//This will help you understand the logic.
//!+""
//p+!
//l+p! ... goes this way
print ( str)
}
return str
}
print (reverse("!pleH"))
Note: If you don't want to use the reversed() in-built function, then you can use the following code.
One-liner using Higher-order function "Reduce" on the string.
extension String {
func reverse() -> String { return self.reduce("") { "\($1)" + $0 } }
}
The function reversed(_:) iterates over each Character in the supplied string s, and simply concenates them in the reverse order.
// 1st pass in the 'for' loop:
// # start of iteration: 'str' equals ""
// update value of 'str' according to
// str = aCharacterAsString + str = "!" + ""
// 2nd pass in the 'for' loop:
// # start of iteration: str equals "!"
// update value of 'str' according to
// str = anotherCharacterAsString + str = "p" + "!"
// 3rd pass in the 'for' loop:
// # start of iteration: str equals "p!"
// update value of 'str' according to
// str = yetAnotherCharacterAsString + str = "l" + "p!"
// and so on ...
// after end of 'for' loop: str == "help!"
// this value of 'str' is then return to callee (which prints it)
A much simpler approach would be using reversed() on the CharacterView of the String instance:
let str = "!pleH"
print(String(str.characters.reversed())) // Help!
Swift 4 You can use it directly in your string
let str = "!pleH"
print(String(str.reversed())) // Help!
var string = "My,playground and my Swift"
var reverseString = ""
for str in string {
reverseString.insert(str, at: reverseString.startIndex)
}
print(reverseString)
In Swift 4 - To reverse string ::
func reverse(string:String) -> String {
var reverse = ""
for char in string {
reverse = char.description + reverse
}
return reverse
}
Input :: reverse(string: "Ashish Chhabra")
Output :: arbahhC hsihsA
var strnew = "hello world new"
var reverseStr = ""
for each in strnew
{
reverseStr = String(each) + reverseStr
}
print(reverseStr)
Different variation of answer using Character and Index.
//Reverse String
func reverse(str:String)->String{
var chars = [Character]()
for i in (0...str.count).reversed() {
let index = str.index(str.startIndex, offsetBy: i)
chars.append(str[index])
}
return String(chars)
}
You can use below code-
let str = "My name is Kplin Cours"
var reverseString = ""
for i in 0..<str.count {
let index = str.index(str.startIndex, offsetBy: (str.count - 1) - i)
// print(String(str[index]))
reverseString = reverseString + String(str[index])
}
print(reverseString) //sruoC nilpK si eman yM
let string = readLine()!
var resultString = ""
for i in 1...string.count {
let index = string.index(string.endIndex, offsetBy: -i)
resultString.append(string[index])
}
print(resultString)
let inputstr = "ABCDEFGHIGKLMNPO"
var resultstr = "";
for strchar in inputstr {
resultstr = String(strchar) + resultstr
}
print("Result = ",resultstr)
Swift 5
extension String {
func invertedEntropy() -> String {
var word = [Character]()
for char in self {
word.insert(char, at: 0)
}
return String(word)
}
}
var palindrome = "TENET"
palindrome.invertedEntropy()
// "TENET"
Simple as that!
let str = "Hello, world!"
let reversed = String(str.reversed())
print(reversed)

Repeating String in Swift

I'm tasked with creating a function that takes two parameters of a string and int that will return a string that repeats the string parameter the int's number of times. This is what I came up with but getting an error. BTW, this is on CodeCardio that I do at work so I haven't been able to test it out in Xcode (my work sucks and uses Windows)
func repeater(aString: String, withNumber: Int) -> String {
let repeatedString = String(count: withNumber, repeatedValue: aString)
return repeatedString
}
String(count: withNumber, repeatedValue: aString)
Is used to instantiate an string with a repeated character: Does Swift init(count:, repeatedValue:) work?
Instead do
func repeater(string: String, withNumber number: Int) -> String {
var repeatedString = String()
for i in 0..<number {
repeatedString += string
}
return repeatedString
}
As of Swift 3, init(repeating:count:) can take String arguments as well.
var repeatingString = String(repeating: "ab", count: 7)
print(repeatingString) // ababababababab
Try this:
func repeater(aString: String, withNumber: Int) -> String {
let repeatedString = String(repeating: aString, count: withNumber)
return repeatedString
}
The type of argument repeatedValue of String(count: repeatedValue: aCharacter) is a single Character rather than String.
You could use Array(count: withNumber, repeatedValue: aString) and join the items
func repeater(aString: String, withNumber: UInt) -> String {
let repeatedArray = Array(count: Int(withNumber), repeatedValue: aString)
return repeatedArray.joinWithSeparator("")
}
You can just do this:
func repeater(aString string: String, withNumber number: Int) -> String {
return [String](count: number, repeatedValue: string).reduce("", combine: +)
}
I slightly modify Tanguy's answer. Now, the function will not crash if the second parameter is less than 0, but returns an empty string
func repeate(string: String, times: Int) -> String {
var repeatedString = ""
var n = times
while n > 0 {
repeatedString += string
n -= 1
}
return repeatedString
}
the same issue has Vadian's answer, checking the parameter and returning early with "" is probably the best solution. I personally prefer Vadian's approach.
func repeatAString(anyString: String, numberOfTimes: Int) ->String
{
var returnString: String = String()
for _ in 1...numberOfTimes
{
returnString = returnString.stringByAppendingString(anyString)
}
return returnString
}
Hi I have used stringByappendingString to solve this one. I see you have plenty of answers but no-one has suggested that so thought Id put it out there. Cheers!
Swift 5:
extension String {
func repeater(by count: Int) -> String {
return String(repeating: self, count: count)
}
}
Calling: "testString".repeater(by: counter)
func repeater(aString: String, withNumber: Int) -> String
{
var repeatedString=""
for var index = 0; index < withNumber; ++index
{
repeatedString=repeatedString+aString
}
return repeatedString
}
for Function Call i use:
print(repeater("String",withNumber: 5))