Reversing string with ignoring characters

Need to reverse strings without affecting characters which user can add by themself. For example i need to reverse abcd123 and ignore "b" and "3" so i need to get 2b1dca3. By ignoring i mean this characters should stay in place and do not swap with others. My code is working only with full word for example

func reverseWithoutFilter(FullText:String, TextToIgnore:String) -> String {
    let fullTextArray = FullText.components(separatedBy: " ")
    let textToIgnoreArray = TextToIgnore.components(separatedBy: " ")
    let result = fullTextArray.map{!textToIgnoreArray.contains($0) ? String($0.reversed()) : $0}
    return result.joined(separator: " ")
}
var result = reverseWithoutFilter(FullText: "FOX is good", TextToIgnore: "FOX")

// result will be "FOX si doog"
Answered by iosNomad in 717787022

Second example


final class ExceptionRuleReverseManager {
  var exceptionElements = String()
   
  func reverse(string: String) -> String {
    let words = string.components(separatedBy: " ")
    var result = [String]()
    for word in words {
      result.append(rearrangeWord(word))
    }
    return String(result.joined(separator: " "))
  }
   
  private func rearrangeWord(_ word: String) -> String {
    var arrayOfCharacters = Array(word)
    if var firstElementIndex = arrayOfCharacters.indices.first,
      var secondElementIndex = arrayOfCharacters.indices.last {
      while firstElementIndex < secondElementIndex {
        if isException(element: word[firstElementIndex]) {
         // If first element is exception - skip it
          firstElementIndex += 1
        // If second element is exception - skip it
        } else if isException(element: word[secondElementIndex]) {
          secondElementIndex -= 1
        } else {
         // If both elements are not exceptions - swap them
          arrayOfCharacters.swapAt(firstElementIndex, secondElementIndex)
          firstElementIndex += 1
          secondElementIndex -= 1
        }
      }
    }
    return String(arrayOfCharacters)
  }
   
  private func isException(element: String.Element) -> Bool {
    return exceptionElements.contains(element)
  }
}

extension String {
  subscript (index: Int) -> String.Element {
    let stringIndex = self.index(self.startIndex, offsetBy: index)
    return self[stringIndex]
  }
}
var ReverseManager = ExceptionRuleReverseManager()
ReverseManager.exceptionElements = "A2"
var input = "Alloha 234"
var result = ReverseManager.reverse(string: input)
print(result)
// result = Aaholl 243
ReverseManager.exceptionElements = "4B"
input = "Ball4 456"
result = ReverseManager.reverse(string: input)
// result = Blla4 465

Have you considered removing the spaces, saving the location of the spaces relative to the beginning, executing the reverse part, then replacing the spaces with the same location but from the end instead of the beginning? I’m not sure how you want your spacing to work with some letters moving and some letters not moving because a letter stating in place may increase the length of the word which would ***** everything up. Try that and let me know what tweaks need to be made and maybe we can brainstorm.

Have a solution but need to optimize

 func reverse(stringWithIgnoreSymbols: String, ignoreSymbols: String) -> String {
    let words = stringWithIgnoreSymbols.components(separatedBy: " ")
    var wordsPositions: [[String:[Int]]] = []
    for (index, word) in words.enumerated() {
      wordsPositions.append([:])
      for ignoredCharacter in ignoreSymbols {
        for (indx, words) in word.enumerated() {
          if words == ignoredCharacter {
            if wordsPositions[index][String(ignoredCharacter)] == nil {
              wordsPositions[index][String(ignoredCharacter)] = [indx]
            } else {
              wordsPositions[index][String(ignoredCharacter)]!.append(indx)
            }
          }
        }
      }
    }
    var newTextArray = words
    for (i, _) in words.enumerated() {
      for ignoredCharacter in ignoreSymbols {
        newTextArray[i] = newTextArray[i].replacingOccurrences(of: String(ignoredCharacter), with: "")
      }
    }
    newTextArray = newTextArray.map() { String($0.reversed()) }
    for i in 0..<words.count {
      var orderedArray = [(Int, String)] ()
      for toRestore in wordsPositions[i] {
        for item in toRestore.value {
          orderedArray.append((item, toRestore.key))
        }
        orderedArray = orderedArray.sorted() { $0.0 < $1.0 }
      }
      for (index, str) in orderedArray {
        let posIndex = newTextArray[i].index(newTextArray[i].startIndex, offsetBy: index)
        let char = str[0]
        newTextArray[i].insert(char, at: posIndex)
      }
    }
    return newTextArray.joined(separator: " ")
  }


private extension StringProtocol {
  subscript(offset: Int) -> Character {
    self[index(startIndex, offsetBy: offset)]
  }
}

I already find the solution

Looks like the solution you "found" is the answer I gave you on SO (https://stackoverflow.com/questions/72616690/reverse-string-without-reversing-custom-characters-swift?noredirect=1#comment128273890_72616690), isn't it ?

Since then, I tried to make the solution more compact and then realised that your spec was not totally clear.

What is textToIgnore ? Is it the whole word (as a substring of the original) or each character in it (that's what your explanation in SO hinted to) ?

For example, what is the expected result with: "A3llohxa 123", excluding "h3". Is it a3xolhlA 213 ? What do you mean by "h" and "3" do not change position ?

If so, that shows that you first have to remove h and 3 in each word (otherwise, what should replace the first l ? Cannot be h, which must not move, so which ?) , reverse each stripped word and then place back h and 3 in the reversed word.

For sure, code may be streamlined (a good exercise for you I guess), but it will have this intrinsic complexity.

Thanks for feedback.

Depending on your spec (but you didn’t answer my question) there may be simpler solutions.

note: a good pratice on the forum is to post solution, not just stating « there is a solution «

Habe a good day, and don’t forget to close the thread.

Accepted Answer

Second example


final class ExceptionRuleReverseManager {
  var exceptionElements = String()
   
  func reverse(string: String) -> String {
    let words = string.components(separatedBy: " ")
    var result = [String]()
    for word in words {
      result.append(rearrangeWord(word))
    }
    return String(result.joined(separator: " "))
  }
   
  private func rearrangeWord(_ word: String) -> String {
    var arrayOfCharacters = Array(word)
    if var firstElementIndex = arrayOfCharacters.indices.first,
      var secondElementIndex = arrayOfCharacters.indices.last {
      while firstElementIndex < secondElementIndex {
        if isException(element: word[firstElementIndex]) {
         // If first element is exception - skip it
          firstElementIndex += 1
        // If second element is exception - skip it
        } else if isException(element: word[secondElementIndex]) {
          secondElementIndex -= 1
        } else {
         // If both elements are not exceptions - swap them
          arrayOfCharacters.swapAt(firstElementIndex, secondElementIndex)
          firstElementIndex += 1
          secondElementIndex -= 1
        }
      }
    }
    return String(arrayOfCharacters)
  }
   
  private func isException(element: String.Element) -> Bool {
    return exceptionElements.contains(element)
  }
}

extension String {
  subscript (index: Int) -> String.Element {
    let stringIndex = self.index(self.startIndex, offsetBy: index)
    return self[stringIndex]
  }
}
var ReverseManager = ExceptionRuleReverseManager()
ReverseManager.exceptionElements = "A2"
var input = "Alloha 234"
var result = ReverseManager.reverse(string: input)
print(result)
// result = Aaholl 243
ReverseManager.exceptionElements = "4B"
input = "Ball4 456"
result = ReverseManager.reverse(string: input)
// result = Blla4 465
Reversing string with ignoring characters
 
 
Q