let input = "hello, world song"
I have an input string as above.
So i can easily use this partial string something like this
output.0 // hello
output.1 // world song
I tried something like this How to split a string by new lines in Swift but i could not exactly what i want.
Can someone write an extension for this please in a nice way?
I would do something like this:
extension String {
func splitAtFirst(_ separator: Character) -> (head: Substring, tail: Substring?) {
guard let indexOfSeparator = self.firstIndex(of: separator) else {
return (head: Substring(self), tail: nil)
}
let indexAfterSeparator = self.index(indexOfSeparator, offsetBy: +1, limitedBy: self.endIndex)!
return (
head: self[..<indexOfSeparator],
tail: self[indexAfterSeparator...]
)
}
}
let (head, tail) = "abc, def, ghi".splitAtFirst(",")
print(head) // abc
print(tail as Any) // Optional(" def, ghi")
This returns Substrings, which gives you an efficient way to do a lot of processing on an input string without causing a bunch of copies along the way. Of course, you should promote these substrings to full on strings after you've finished processing them.
You can use an extension as below:
extension StringProtocol {
var tupleOfSplittedString: (String,String) {
if !self.isEmpty {
let splitted = self.split(separator: ",").map { String($0)}
let firstPart = splitted[0]
let otherPart = String(splitted[1...].joined().dropFirst())
return (firstPart,otherPart)
}
return ("","")
}
}
let input = "hello, world song"
let resultOfFirstPart = input.tupleOfSplittedString.0 // hello
let resultOfOtherPart = input.tupleOfSplittedString.1 // world song
This code uses the .components method to split a string by a substring. I have tested this, and it was successful, even if the String is blank (it returns ("", "") in that case)
Line 4 of this code is a bit hard to read, but you can split it up into multiple lines of code if you would like.
import Foundation
extension String {
var tuple: (String, String) {
return self.components(separatedBy: ",").count == 2 ? ((self.components(separatedBy: ",")[0], self.components(separatedBy: ",")[1] )) : ("", "")
}
}
var input = "hello, world song"
print(input.tuple.0) //prints hello
print(input.tuple.1) //prints world song
Related
I am stuck about this goal.
Learn about the map method, and use it in place of the loop that converts the array of characters to an array of strings in updateUI().
I have read Documentation and topics about map, but still all my tries did not work.
//change loop below to map method
for letter in currentGame.formattedWord {
letters.append(String(letter))
}
let wordWithSpacing = letters.joined(separator: " ")
correctWordLabel.text = wordWithSpacing
scoreLabel.text = "Wins: \(totalWins), Losses: \(totalLosses)"
treeImageView.image = UIImage(named: "Tree \(currentGame.incorrectMovesRemaining)")
Thanks for help
The String documentation tells us:
A string is a series of characters, such as "Swift", that forms a collection.
So, you can use map to convert each of these characters to individual strings:
let string = "Hello, world"
let letters = string.map { (character: Character) -> String in
return String(character)
}
Or, more concisely:
let letters = string.map { String($0) }
I did it. the problem was that I left the var letters as an empty array and tried to add characters to this array. Now I realized that this was wrong.
func updateUI() {
let letters = currentGame.formattedWord
let mappedLetters = letters.map { String($0)}
/* for letter in currentGame.formattedWord {
letters.append(String(letter))
} */
Thanks all!
I am trying to learn swift by solving interview questions. One of the question that I am trying to solve is as follows.
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
Example:
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
My implementation is as follows which holds t string characters and its corresponding index retrieved from s.
func minimumWindowSubstring(_ s: String, _ t: String) -> String{
let sChar = [Character](s)
let tChar = [Character](t)
var indexTemp = [[Character:Int]()]
for tc in tChar
{
for (j, sc) in sChar.enumerated()
{
if sc == tc
{
indexTemp.append([tc:j])
}
}
}
return ""
}
what I have in indexTemp array is as follows
Now I wonder how could I able to use this array to find the minimumwindow, I stuck.
I thought it was an interesting problem so I gave it a shot. Instead of using a dictionary I used a simple class to store the range of characters found, as well as a String that stores which characters haven't been found.
It only goes through the main string once, so it should be O(n).
You can run this in the playground.
(I know you wanted help in fixing your code and my answer doesn't do that, but I'm hoping it will provide enough insight for you to adjust your own code)
import Foundation
let string = "ADOBECODEBANC"
let sub = "ABC"
// Create a class to hold the start and end index of the current search range, as well as a remaining variable
// that will store which characters from sub haven't been found
class Container {
var start: Int
var end: Int?
var remaining: String
// Consume will attempt to find a given character within our remaining string, if it has found all of them,
// it will store the end index
func consume(character: Character, at index: Int) {
// If remaining is empty, we're done
guard remaining != "" else { return }
// We're assuming that sub won't have repeating characters. If it does we'll have to chage this
remaining = remaining.replacingOccurrences(of: String(character), with: "")
if remaining == "" {
end = index
}
}
init(start: Int, remaining: String) {
self.start = start
self.remaining = remaining
}
}
// ClosedContainer is similar to Container, but it can only be initialized by an existing container. If the existing
// container doesn't have an end value, the initialization will fail and return nil. This way we can weed out containers
// for ranges where we didn't find all characters.
class ClosedContainer {
let start: Int
let end: Int
init?(container: Container) {
guard let end = container.end else { return nil }
self.start = container.start
self.end = end
}
var length: Int {
return end - start
}
}
var containers = [Container]()
// Go through each character of the string
string.enumerated().forEach { index, character in
// Look for matches in sub
if sub.contains(character) {
// Allow each existing container to attempt to consume the character
containers.forEach { container in
container.consume(character: character, at: index)
}
// Create a new container starting on this index. It's remaining value will be the sub string without the
// character we just found
let container = Container(start: index, remaining: sub.replacingOccurrences(of: String(character), with: ""))
containers.append(container)
}
}
// Convert Containers into ClosedContainers using compactMap, then find the one with the shortest length
let closedContainers = containers.compactMap(ClosedContainer.init)
let maybeShortest = closedContainers.min { $0.length < $1.length }
if let shortest = maybeShortest {
// Convert int to String indices
let start = string.index(string.startIndex, offsetBy: shortest.start)
let end = string.index(string.startIndex, offsetBy: shortest.end)
// Get the result string
let result = string[start...end]
print("Shortest substring of", string, "that contains", sub, "is", result)
} else {
// No range was found that had all characters in sub
print(string, "doesn't contain all characters in", sub)
}
I want to write a function that could be used like this:
let ๐ฉโ๐ฉโ๐งโ๐ฆ = "๐ฉโ๐ฉโ๐งโ๐ง".replacingFirstOccurrence(of: "๐ง", with: "๐ฆ")
Given how odd both this string and Swift's String library are, is this possible in Swift?
Based on the insights gained at Why are emoji characters like ๐ฉโ๐ฉโ๐งโ๐ฆ treated so strangely in Swift strings?, a sensible approach might be to replace Unicode scalars:
extension String {
func replacingFirstOccurrence(of target: UnicodeScalar, with replacement: UnicodeScalar) -> String {
let uc = self.unicodeScalars
guard let idx = uc.index(of: target) else { return self }
let prefix = uc[uc.startIndex..<idx]
let suffix = uc[uc.index(after: idx) ..< uc.endIndex]
return "\(prefix)\(replacement)\(suffix)"
}
}
Example:
let family1 = "๐ฉโ๐ฉโ๐งโ๐ฆ"
print(family1.characters.map { Array(String($0).unicodeScalars) })
// [["\u{0001F469}", "\u{200D}"], ["\u{0001F469}", "\u{200D}"], ["\u{0001F467}", "\u{200D}"], ["\u{0001F466}"]]
let family2 = family1.replacingFirstOccurrence(of: "๐ง", with: "๐ฆ")
print(family2) // ๐ฉโ๐ฉโ๐ฆโ๐ฆ
print(family2.characters.map { Array(String($0).unicodeScalars) })
// [["\u{0001F469}", "\u{200D}"], ["\u{0001F469}", "\u{200D}"], ["\u{0001F466}", "\u{200D}"], ["\u{0001F466}"]]
And here is a possible version which locates and replaces the Unicode scalars of an arbitrary string:
extension String {
func replacingFirstOccurrence(of target: String, with replacement: String) -> String {
let uc = self.unicodeScalars
let tuc = target.unicodeScalars
// Target empty or too long:
if tuc.count == 0 || tuc.count > uc.count {
return self
}
// Current search position:
var pos = uc.startIndex
// Last possible position of `tuc` within `uc`:
let end = uc.index(uc.endIndex, offsetBy: tuc.count - 1)
// Locate first Unicode scalar
while let from = uc[pos..<end].index(of: tuc.first!) {
// Compare all Unicode scalars:
let to = uc.index(from, offsetBy: tuc.count)
if !zip(uc[from..<to], tuc).contains(where: { $0 != $1 }) {
let prefix = uc[uc.startIndex..<from]
let suffix = uc[to ..< uc.endIndex]
return "\(prefix)\(replacement)\(suffix)"
}
// Next search position:
uc.formIndex(after: &pos)
}
// Target not found.
return self
}
}
Using the range(of:options:range:locale:) the solution became quite concise:
extension String {
func replaceFirstOccurrence(of searchString: String, with replacementString: String) -> String {
guard let range = self.range(of: searchString, options: .literal) else { return self }
return self.replacingCharacters(in: range, with: replacementString)
}
}
This works by first finding the range of searchString within the instance, and if a range is found the range is replaced with replacementString. Otherwise the instance just returns itself. And, since the range(of:) method returns as soon as it finds a match, the returned range is guaranteed to be the first occurrence.
"221".replaceFirstOccurrence(of: "2", with: "3") // 321
"๐ฉโ๐ฉโ๐งโ๐ฆ".replaceFirstOccurrence(of: "\u{1f469}", with: "\u{1f468}") // ๐จโ๐ฉโ๐งโ๐ฆ
*To clarify, the last test case converts woman-woman-girl-boy to man-woman-girl-boy.
a string such as ! !! yuahl! ! , I want delete ! and , when I code like this
for index in InputName.characters.indices {
if String(InputName[index]) == "" || InputName.substringToIndex(index) == "!" {
InputName.removeAtIndex(index)
}
}
have this error " fatal error: subscript: subRange extends past String end ", how should I do? THX :D
Swift 5+
let myString = "aaaaaaaabbbb"
let replaced = myString.replacingOccurrences(of: "bbbb", with: "") // "aaaaaaaa"
If you need to remove characters only on both ends, you can use stringByTrimmingCharactersInSet(_:)
let delCharSet = NSCharacterSet(charactersInString: "! ")
let s1 = "! aString! !"
let s1Del = s1.stringByTrimmingCharactersInSet(delCharSet)
print(s1Del) //->aString
let s2 = "! anotherString !! aString! !"
let s2Del = s2.stringByTrimmingCharactersInSet(delCharSet)
print(s2Del) //->anotherString !! aString
If you need to remove characters also in the middle, "reconstruct from the filtered output" would be a little bit more efficient than repeating single character removal.
var tempUSView = String.UnicodeScalarView()
tempUSView.appendContentsOf(s2.unicodeScalars.lazy.filter{!delCharSet.longCharacterIsMember($0.value)})
let s2DelAll = String(tempUSView)
print(s2DelAll) //->anotherStringaString
If you don't mind generating many intermediate Strings and Arrays, this single liner can generate the expected output:
let s2DelAll2 = s2.componentsSeparatedByCharactersInSet(delCharSet).joinWithSeparator("")
print(s2DelAll2) //->anotherStringaString
I find that the filter method is a good way to go for this sort of thing:
let unfiltered = "! !! yuahl! !"
// Array of Characters to remove
let removal: [Character] = ["!"," "]
// turn the string into an Array
let unfilteredCharacters = unfiltered.characters
// return an Array without the removal Characters
let filteredCharacters = unfilteredCharacters.filter { !removal.contains($0) }
// build a String with the filtered Array
let filtered = String(filteredCharacters)
print(filtered) // => "yeah"
// combined to a single line
print(String(unfiltered.characters.filter { !removal.contains($0) })) // => "yuahl"
Swift 3
In Swift 3, the syntax is a bit nicer. As a result of the Great Swiftification of the old APIs, the factory method is now called trimmingCharacters(in:). Also, you can construct the CharacterSet as a Set of single-character Strings:
let string = "! !! yuahl! !"
string.trimmingCharacters(in: [" ", "!"]) // "yuahl"
If you have characters in the middle of the string you would like to remove as well, you can use components(separatedBy:).joined():
let string = "! !! yu !ahl! !"
string.components(separatedBy: ["!", " "]).joined() // "yuahl"
H/T #OOPer for the Swift 2 version
func trimLast(character chars: Set<Character>) -> String {
let str: String = String(self.reversed())
guard let index = str.index(where: {!chars.contains($0)}) else {
return self
}
return String((str[index..<str.endIndex]).reversed())
}
Note:
By adding this function in String extension, you can delete the specific character of string at last.
for index in InputName.characters.indices.reversed() {
if String(InputName[index]) == "" || InputName.substringToIndex(index) == "!" {
InputName.removeAtIndex(index)
}
}
Also you can add such very helpful extension :
import Foundation
extension String{
func exclude(find:String) -> String {
return stringByReplacingOccurrencesOfString(find, withString: "", options: .CaseInsensitiveSearch, range: nil)
}
func replaceAll(find:String, with:String) -> String {
return stringByReplacingOccurrencesOfString(find, withString: with, options: .CaseInsensitiveSearch, range: nil)
}
}
you can use this:
for example if you want to remove "%" the percent from 10%
if let i = text.firstIndex(of: "%") {
text.remove(at: i) //10
}
I wrote extension that create split method:
extension String {
func split(splitter: String) -> Array<String> {
return self.componentsSeparatedByString(splitter)
}
}
So in playground I can write:
var str = "Hello, playground"
if str.split(",").count > 1{
var out = str.split(",")[0]
println("output: \(out)") // output: Hello
}
What do I need to make it work with regex like in Java:
str.split("[ ]+")
Because this way it doesn't work.
Thanks,
First, your split function has some redundancy. It is enough to return
return self.componentsSeparatedByString(splitter)
Second, to work with a regular expression you just have to create a NSRegularExpression and then perhaps replace all occurrences with your own "stop string" and finally separate using that. E.g.
extension String {
func split(regex pattern: String) -> [String] {
let template = "-|*~~*~~*|-" /// Any string that isn't contained in the original string (self).
let regex = try? NSRegularExpression(pattern: pattern)
let modifiedString = regex?.stringByReplacingMatches(
in: self,
range: NSRange(
location: 0,
length: count
),
withTemplate: template /// Replace with the template/stop string.
)
/// Split by the replaced string.
return modifiedString?.components(separatedBy: template) ?? []
}
}