38

How could I replace nth character of a String with another one?

func replace(myString:String, index:Int, newCharac:Character) -> String {
    // Write correct code here
    return modifiedString
}

For example, replace("House", 2, "r") should be equal to "Horse".

2
  • This is similar to this question. It has answer too stackoverflow.com/questions/6843441/… Commented Jul 16, 2014 at 20:04
  • 3
    @ShashiJeevanM.P.: Similar question, but different language: Swift is not JavaScript. Commented Jul 16, 2014 at 21:03

16 Answers 16

38

Solutions that use NSString methods will fail for any strings with multi-byte Unicode characters. Here are two Swift-native ways to approach the problem:

You can use the fact that a String is a sequence of Character to convert the string to an array, modify it, and convert the array back:

func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
    var chars = Array(myString)     // gets an array of characters
    chars[index] = newChar
    let modifiedString = String(chars)
    return modifiedString
}

replace("House", 2, "r")
// Horse

Alternately, you can step through the string yourself:

func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
    var modifiedString = String()
    for (i, char) in myString.characters.enumerate() {
        modifiedString += String((i == index) ? newChar : char)
    }
    return modifiedString
}

Since these stay entirely within Swift, they're both Unicode-safe:

replace("🏠🏡🏠🏡🏠", 2, "🐴")
// 🏠🏡🐴🏡🏠
Sign up to request clarification or add additional context in comments.

3 Comments

String.characters() is deprecated in swift 4. Alternate ?
@AwaisFayyaz in Swift 4 you can change var chars = Array(myString.characters) to var chars = Array(myString) to fix the deprecation warning. Source: stackoverflow.com/a/25921323/62
This causes at least 1 copy (if not more), while the Swift team didn't allow the feature just to prevent that !! Anyone interested in copy-free version see my answer (to only edit one node in the linked-list, which String internally could be, instead of copying)
23

I've found this solution.

var string = "Cars"
let index = string.index(string.startIndex, offsetBy: 2)
string.replaceSubrange(index...index, with: "t")
print(string)
// Cats

Comments

22

In Swift 4 it's much easier.

let newString = oldString.prefix(n) + char + oldString.dropFirst(n + 1)

This is an example:

let oldString = "Hello, playground"
let newString = oldString.prefix(4) + "0" + oldString.dropFirst(5)

where the result is

Hell0, playground

The type of newString is Substring. Both prefix and dropFirst return Substring. Substring is a slice of a string, in other words, substrings are fast because you don't need to allocate memory for the content of the string, but the same storage space as the original string is used.

2 Comments

How do you do the same for a Substring?
Exactly the same. You can replace the first line of the sample with let oldString: Substring = "Hello, playground" and it works fine.
8

Please see NateCook answer for more details

func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
    var chars = Array(myString.characters)     // gets an array of characters
    chars[index] = newChar
    let modifiedString = String(chars)
    return modifiedString
}

For Swift 5

func replace(myString: String, _ index: Int, _ newChar: Character) -> String {
    var chars = Array(myString)     // gets an array of characters
    chars[index] = newChar
    let modifiedString = String(chars)
    return modifiedString
}

replace("House", 2, "r")

This is no longer valid and deprecated.

You can always use swift String with NSString.So you can call NSString function on swift String. By old stringByReplacingCharactersInRange: you can do like this

var st :String = "House"
let abc = st.bridgeToObjectiveC().stringByReplacingCharactersInRange(NSMakeRange(2,1), withString:"r") //Will give Horse

2 Comments

what is bridgeToObjectiveC()
This causes at least 1 copy (if not more), while the Swift team didn't allow the feature just to prevent that !! Anyone interested in copy-free version see my answer (to only edit one node in the linked-list, which String internally could be, instead of copying)
3

To modify existing string:

extension String {
    subscript(_ n: Int) -> Character {
        get {
            let idx = self.index(startIndex, offsetBy: n)
            return self[idx]
        }
        set {
            let idx = self.index(startIndex, offsetBy: n)
            self.replaceSubrange(idx...idx, with: [newValue])
        }
    }
}


var s = "12345"
print(s[0]) 
s[0] = "9"
print(s) 

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
1

I've expanded upon Nate Cooks answer and transformed it into a string extension.

extension String {

    //Enables replacement of the character at a specified position within a string
    func replace(_ index: Int, _ newChar: Character) -> String {
        var chars = Array(characters)
        chars[index] = newChar
        let modifiedString = String(chars)
        return modifiedString
    }
}

usage:

let source = "House"
let result = source.replace(2,"r")

result is "Horse"

Comments

1

I think what @Greg was trying to achieve with his extension is this:

mutating func replace(characterAt index: Int, with newChar: Character) {
    var chars = Array(characters)
    if index >= 0 && index < self.characters.count {
        chars[index] = newChar
        let modifiedString = String(chars)
        self = modifiedString
    } else {
        print("can't replace character, its' index out of range!")
    }
}

usage:

let source = "House"
source.replace(characterAt: 2, with: "r") //gives you "Horse"

Comments

0

After looking at the Swift Docs, I managed to make this function:

//Main function
func replace(myString:String, index:Int, newCharac:Character) -> String {
    //Looping through the characters in myString
    var i = 0
    for character in myString {
        //Checking to see if the index of the character is the one we're looking for
        if i == index {
            //Found it! Now instead of adding it, add newCharac!
            modifiedString += newCharac
        } else {
            modifiedString += character
        }
        i = i + 1
    }
    // Write correct code here
    return modifiedString
}

Please note that this is untested, but it should give you the right idea.

Comments

0
func replace(myString:String, index:Int, newCharac:Character) -> String {

    var modifiedString = myString
    let range = Range<String.Index>(
        start: advance(myString.startIndex, index),
        end: advance(myString.startIndex, index + 1))
    modifiedString.replaceRange(range, with: "\(newCharac)")
    return modifiedString
}

I would prefer to pass a String than a Character though.

Comments

0

Here's a way to replace a single character:

var string = "This is the original string."
let offset = 27
let index = string.index(string.startIndex, offsetBy: offset)
let range = index...index
print("ORIGINAL string: " + string)
string.replaceSubrange(range, with: "!")
print("UPDATED  string: " + string)

// ORIGINAL string: This is the original string.
// UPDATED  string: This is the original string!

This works with multi-character strings as well:

var string = "This is the original string."
let offset = 7
let index = string.index(string.startIndex, offsetBy: offset)
let range = index...index
print("ORIGINAL string: " + string)
string.replaceSubrange(range, with: " NOT ")
print("UPDATED  string: " + string)

// ORIGINAL string: This is the original string.
// UPDATED  string: This is NOT the original string.

Comments

0
        var s = "helloworld"
        let index = ((s.count) / 2) // index is 4
        let firstIndex = s.index(s.startIndex, offsetBy: index)
        let secondIndex = s.index(s.startIndex, offsetBy: index)
        s.replaceSubrange(firstIndex...secondIndex, with: "*")
        print("Replaced string is: \(s)") //OUTPUT IS: hell*world

This is working fine to replace string using the index.

Comments

0

String class in Swift (till v5 and maybe later) 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).

Comments

0
public void createEncodedSentence() {

    StringBuffer buff = new StringBuffer();
    int counter = 0;
    char a;

    for (int i = 0; i < sentence.length(); i++) {
        a = sentence.charAt(i);

        if (a == '.') {
            buff.append('*');
        }
        if (a != ' ' && a != '.') {
            counter++;
        }
        if (counter % 3 == 0) {
            buff.append("");
        }
        buff.append(sentence.charAt(i));


    }

    encodedSentence = buff.toString();

}

Comments

0

/* Replace Every Nth charater in String with another character*/

    func replaceAllNthChar(inputString:String, nthChar: Int, replaceBy:Character) -> String {
    var arrStr = Array(inputString)
    let n = nthChar
    
    for i in stride(from: (n - 1), to: arrStr.count - 1, by: n) {
        arrStr[i] = replaceBy
    }
    return String(arrStr)
}

print(replaceNthChar(inputString: "abcdefghijklmop", nthChar: 4, replaceBy: "_"))

Comments

-1

Strings in swift don't have an accessor to read or write a single character. There's an excellent blog post by Ole Begemann describing how strings in swift work.

Note: the implementation below is wrong, read addendum

So the right way is by taking the left part of the string up to the index -1 character, append the replacing character, then append the string from index + 1 up to the end:

func myReplace(myString:String, index:Int, newCharac:Character) -> String {
    var modifiedString: String

    let len = countElements(myString)

    if (index < len) && (index >= 0) {
        modifiedString = myString.substringToIndex(index) + newCharac + myString.substringFromIndex(index + 1)
    } else {
        modifiedString = myString
    }

    return modifiedString
}

Note: in my implementation I chose to return the original string if the index is not in a valid range

Addendum Thanks to @slazyk, who found out that my implementation is wrong (see comment), I am providing a new swift only version of the function.

func replace(myString:String, index:Int, newCharac:Character) -> String {
    var modifiedString: String

    if (index < 0) || (index >= countElements(myString)) {
        modifiedString = myString
    } else {
        var start = myString.startIndex
        var end = advance(start, index)

        modifiedString = myString[start ..< end]
        modifiedString += newCharac

        start = end.successor()
        end = myString.endIndex

        modifiedString += myString[start ... end]
    }

    return modifiedString
}

@codester's answer looks very good, and it's probably what I would use myself. It would be interesting to know how performances compare though, using a fully swift solution and bridging to objective-c instead.

2 Comments

Actually, your code is wrong. And you can even find out why in the blog post you are referring to. The substringToIndex and substringFromIndex are NSString methods, and they use indices as defined for NSString, on the other hand you calculate len using countElements. You should either stick with NSStrings indexing and length (utf16count in Swift) or Strings. They do differ.
Ok my bad, I didn't check the source of what intellisense suggested me. Thanks for pointing that out, I appreciate it and I accept the downvote.
-1

Here is an efficient answer :

import Foundation
func replace(myString:String, index:Int, newCharac:Character) -> String {
return myString.substringToIndex(index-1) + newCharac + myString.substringFromIndex(index)
}

2 Comments

substringToIndex() and substringFromIndex() use NSString indexing, and thus aren't Unicode safe.
You're right, but I'm only dealing with alphabetic characters. I should have told it, however.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.