1

I have an array of Strings. For example, this:

var stringArray1 = ["abcdef", "bcdefg", "cdefgh"]

I want to mutate the array and take out the first 3 characters of each string. The result would be this:

var newStringArray1 = ["def", "efg", "fgh"]

Taking from Apple's Documentation, I tried this:

func mutateArray(x: [String]) -> [String] {
    var newArray = [String]()
    for i in x {
        let range =  i.startIndex.advancedBy(3)
        i.removeRange(range)
        newArray.append(i)
    }
    return newArray
}

but this line:

i.removeRange(range)

gave me an error: "Cannot use mutating member on mutable value: "i" is a "let" constant."

how can I change the array like this? I have heard it is possible with the map() function as well, but when searching about the map function, most of the explanations I received were from swift 1/1.2, and it changed in swift 2.

thanks

2
  • and if you set i as var, the program will crash ... i is index in the array, not in the strings inside! Commented Nov 14, 2015 at 9:12
  • oh i doesn't represent the contents of the index, but the index itself. that makes sense as to why I got the error then. thanks. Commented Nov 14, 2015 at 11:53

3 Answers 3

2

Two solutions:

  • using substringFromIndex

    func mutateArray(x: [String]) -> [String] {
      var newArray = [String]()
      for i in x {
        newArray.append(i.substringFromIndex(i.startIndex.advancedBy(3)))
      }
      return newArray
    }
    
    
    let stringArray1 = ["abcdef", "bcdefg", "cdefgh"]
    let trimmedStringArray = mutateArray(stringArray1)
    
  • using map

    let trimmedStringArray = stringArray1.map {$0.substringFromIndex($0.startIndex.advancedBy(3))}
    

In both cases you should check for the length of the input string if it's not guaranteed that there are always at least 3 characters.

Sign up to request clarification or add additional context in comments.

Comments

1

You can use the powerful map function this way:

func mutatedArray(array: [String]) -> [String] {
    return array.map {
        $0.substringFromIndex($0.startIndex.advancedBy(3))
    }
}

Tests:

var list0 = ["abcdef", "bcdefg", "cdefgh"]
mutatedArray(list0) // ["def", "efg", "fgh"]

var list1 = ["def", "efg", "fgh"]
mutatedArray(list1) // ["", "", ""]

Extension

You can also write the function as an extension available for Arrays of Strings.

extension SequenceType where Generator.Element == String {
    func mutatedArray(removeChars: Int) -> [String] {
        return self.map {
            $0.substringFromIndex($0.startIndex.advancedBy(removeChars))
        }
    }
}

Test:

var list1 = ["def", "efg", "fgh"]
list1.mutatedArray(2) // ["f", "g", "h"]

Note

Please note I changed the name of the function to mutatedArray since it is returning a new array and it's not changing the original one.

3 Comments

lots of cool stuff here I didn't know you could do in swift! thanks.
is the "extension SequenceType where Generator.Element == String" any different than "Extension String : { // code here }" syntax?
The difference is that if you write extension String { ... } you are adding something to String. On the other hand with extension SequenceType where Generator.Element == String { ... }you are adding functionalities to the SequenceType protocol when the element of the sequence is a String (e.g. an Array of String because Array does conform to SequenceType). This is part of the beauty of a Protocol-Oriented language like Swift :-)
0

You can't modify the "counter" (not really a counter in this case) variable in a for in loop. Use a normal loop and create a new object.

//standard for
for (var j = 0; j < x.length; j++) {
    var i: String = x[i] //copy contents of object
    let range =  i.startIndex.advancedBy(3)
    i.removeRange(range)
    newArray.append(i)
}

Comments

Your Answer

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