Home > Software engineering >  Exclution implementation in reversing string in Swift
Exclution implementation in reversing string in Swift

Time:08-07


Hello guys!!

Need some help!!

In ViewController - I have two text fields, like text field for text wich user wants to reverse and text field for exclusions, and result-UILabel wich shows the result.

In first text field user typing some text for reverse and result-UILabel shows the result of reversed text.

In the second text field, I want to make an exception for letters wich shouldnt be reversed in result-UILabel. They should be untouchable in the word of the reverse at the time of reversing the text from the first field and should remain in their places.

The model function is in another swift file in another class.


Model function:

import Foundation

class ReverseWords {

public func reverse(textField: String) -> String {
    
    if textField.isEmpty {
        return ""
    }
    
    return textField.trimmingCharacters(in: .whitespacesAndNewlines)
        .components(separatedBy: " ")
        .map { String ( $0.reversed() ) }
        .joined(separator: " ")
    }
}

Using model function for first text field in ViewController:

resultLabel.text = reverseWords.reverse(textField:
reverseTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines))

Example:

First text field (for reverse) print:

FogmEistEr

Second text field (for exclusions) letters wich should be untouchable of reverse, print:

E

And result label shows:

rtsiEmgoEF

How can I implement this?

How can I call exceptionTextField in model to check his characters inside? Actualy, I don't want to do this between classes, but I would like to look at the solution.

I think.., it would be better to do it in ViewController, but I got confused…))

Have you any ideas, how to implement this?

Thnx you all!!


CodePudding user response:

Here you can take reference for logic (It can be optimized). We can pass exclude reverse function. Rather than put this logic in the view controller I would suggest keeping the logic in ReverseWords class.

class ReverseWords {
    
    public func reverse(textField: String, excludeWord: String) -> String {
        
        if textField.isEmpty {
            return ""
        }
        
        return textField.trimmingCharacters(in: .whitespacesAndNewlines)
            .components(separatedBy: " ")
            .map { $0 == excludeWord ? String($0) : reverseWordWithExclude(currentWord: String($0), excludeWord: excludeWord) }
            .joined(separator: " ")
    }
    
    private func reverseWordWithExclude(currentWord: String, excludeWord: String) -> String {

  // return direct reversed string if the exclude word not contain in string
        if !currentWord.contains(excludeWord) {
            return String(currentWord.reversed())
        }
        
        // Replace whole exception word with single space char which is never included in the current word.
        // Easy to find the current index for a single space or for a char.
        let replaceWord = currentWord.replacingOccurrences(of: excludeWord, with: " ")
        
        var reverseString = ""
        var exceptionIndexes: [Int] = []
        
        // Find the index of exclude word and reverse string without include exclude word
        replaceWord.enumerated().forEach { index, char in
            if char == " " {
                exceptionIndexes.append(index)
            } else {
                reverseString.insert((char), at: reverseString.startIndex)
            }
        }
        
        // Now replace single space with actual exclude word at their position.
        exceptionIndexes.forEach{ index in
            reverseString.insert(contentsOf: excludeWord, at: reverseString.index(reverseString.startIndex, offsetBy: index))
        }
        
        return reverseString
    }
}

CodePudding user response:

I'd just make an extension on String:

extension String {
    func reversed(omittingCharactersIn characterSet: CharacterSet) -> String {
        var reversed = reversed()
            .filter { String($0).rangeOfCharacter(from: characterSet) == nil }
        let excluded = enumerated()
            .filter { String($0.element).rangeOfCharacter(from: characterSet) != nil }
        for (i, char) in excluded {
            reversed.insert(char, at: reversed.index(reversed.startIndex, offsetBy: i))
        }
        return String(reversed)
    }
}

All this is doing is reversing all of the characters right away, but removing any characters that should not be reversed. Then it finds and maintains the indices and characters should not move. Finally, it inserts the characters that should not be reversed back to their original position.

Usage:

let text = "FogmEistEr"
let omit = "E"
print(text.reversed(omittingCharactersIn: CharacterSet(charactersIn: omit)))

Prints:

rtsiEmgoEF

Here's how I'd plug it into a viewController assuming the reversal would happen either by a button push or by the text simply changing:

class ViewController: UIViewController {
    @IBOutlet weak var originalTextField: UITextField!

    @IBOutlet weak var exclusionTextField: UITextField!

    @IBOutlet weak var reversedLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        originalTextField.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
        exclusionTextField.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
    }

    @objc func textChanged(_ sender: UITextField) {
        tryReverse()
    }

    @IBAction func didTapButton(_ sender: UIButton) {
        tryReverse()
    }

    private func tryReverse() {

        guard let originalText = originalTextField.trimmedTextNilIfEmpty,
              let exclusionText = exclusionTextField.trimmedTextNilIfEmpty else {
            reversedLabel.text = ""
            return
        }

        reversedLabel.text = originalText.components(separatedBy: " ")
            .map {
                $0.reversed(omittingCharactersIn: CharacterSet(charactersIn: exclusionText))
            }.joined(separator: " ")
    }
}

extension UITextField {
    var trimmedTextNilIfEmpty: String? {
        if let trimmed = text?.trimmingCharacters(in: .whitespacesAndNewlines) {
            return trimmed.isEmpty ? nil : trimmed
        }
        return nil
    }
}
  • Related