split string into array with a common regx - swift

In Swift I have a string like:
let str = "5~895893799,,6~898593679,,7~895893679,,8~895893799,,5~895893799,,6~898593679,,7~895893679,,8~895893799";
From this I need to get only the number which is before "~" which is [5,6,7,8,5,6,7,8]
How can I achieve this?

Make use of components(separatedBy:) and compactMap.
let str = "5~895893799,,6~898593679,,7~895893679,,8~895893799,,5~895893799,,6~898593679,,7~895893679,,8~895893799"
let nums = str.components(separatedBy: ",,")
.compactMap { $0.components(separatedBy: "~").first }
That gives the string array:
["5", "6", "7", "8", "5", "6", "7", "8"]
If you want an array of Int, add:
.compactMap { Int($0) }
to the end.
let nums = str.components(separatedBy: ",,")
.compactMap { $0.components(separatedBy: "~").first }
.compactMap { Int($0) }
That gives:
[5, 6, 7, 8, 5, 6, 7, 8]

You could define this regular expression:
let regex = try! NSRegularExpression(pattern: #"\d+(?=~)"#)
\d : matches any digit
+ : this is the one or more quantifier
(?=~) : Positive lookahead for "~"
and use it like so:
let range = NSRange(location: 0, length: (str as NSString).length)
let matches = regex.matches(in: str, range: range)
let strings: [String] = matches.map { match in
let subRange = match.range
let startIndex = str.index(str.startIndex, offsetBy: subRange.lowerBound)
let endIndex = str.index(str.startIndex, offsetBy: subRange.upperBound)
let subString = String(str[startIndex..<endIndex])
return subString
}
let numbers = strings.compactMap(Int.init) //[5, 6, 7, 8, 5, 6, 7, 8]

Related

Range position of letter in string duplicating [duplicate]

This question already has answers here:
Index of a substring in a string with Swift
(11 answers)
Closed 4 years ago.
I have a string like "abc1abc1".
What I want to do is bold each number of the string. I have drawn the code below. It works by separating each character of the string and putting them into an array. Then, in a loop, if each character contains an Int(), the character is bolded.
However, the issue comes when the there are two of the same Int. In the string above, there are 2 characters of 1, therefore the code will only bold the first character.
// Bold the numbers
let fullString = "abc1abc1"
let characters = Array(fullString)
let mutableString = NSMutableAttributedString(string: fullString)
for item in characters {
let string = String(item)
let decimalCharacters = CharacterSet.decimalDigits
let decimalRange = string.rangeOfCharacter(from: decimalCharacters)
if decimalRange != nil {
let str = NSString(string: fullString)
let range = str.range(of: string)
mutableString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIFont.systemFont(ofSize: 18, weight: .heavy), range: range)
}
}
instructionsLabel.attributedText = mutableString
// characters = ["a", "b", "c", "1", "a", "b", "c", "1"]
// range of ints returns {3, 1} and {3, 1}
// range of ints SHOULD return {3, 1} and {7, 1}
Try this:
let fullString = "abc1abc1"
let range = NSRange(location: 0, length: (fullString as NSString).length)
let mutableString = NSMutableAttributedString(string: fullString)
let regex = try! NSRegularExpression(pattern: "[0-9]")
let matches = regex.matches(in: fullString, range: range)
for match in matches {
mutableString.addAttributes([.font: UIFont.systemFont(ofSize: 18, weight: .heavy)], range: match.range)
}
instructionsLabel.attributedText = mutableString

Extract the elements from array and put back them to another array.?

suppose i have a array that have 10 elements. say,
var ArrayElemts : ["1","2","3","4","5","6","7","8","9","10","11"]
Now how can i keep the elements from 0 t0 5 in one array set and 6 to 10 to another array set?
Use [0...5] to create an ArraySlice and then Array to convert that back to an array:
var arrayElemts = ["1","2","3","4","5","6","7","8","9","10","11"]
let first = Array(arrayElemts[0...5])
let second = Array(arrayElemts[6...10])
print(first) // ["1", "2", "3", "4", "5", "6"]
print(second) // ["7", "8", "9", "10", "11"]
The easiest option is the following:
let partition1 = array.filter { Int($0) ?? 0 <= 5 }
let partition2 = array.filter { Int($0) ?? 0 > 5 }
Conversion to numbers should be the first step though. You should never work with strings as if they were numbers.
let numbers = array.flatMap { Int($0) }
let partition1 = numbers.filter { $0 <= 5 }
let partition2 = numbers.filter { $0 > 5 }
If we suppose the array is sorted, there are easier options:
let sorted = numbers.sorted()
let partition1: [Int]
let partition2: [Int]
if let partition2start = sorted.index(where: { $0 > 5 }) {
partition1 = Array(sorted.prefix(upTo: partition2start))
partition2 = Array(sorted.suffix(from: partition2start))
} else {
partition1 = sorted
partition2 = []
}
which is what the native partition method can do:
var numbers = array.flatMap { Int($0) }
let index = numbers.partition { $0 > 5 }
let partition1 = Array(numbers.prefix(upTo: index))
let partition2 = Array(numbers.suffix(from: index))
Note the method changes the original array.
Breaking the array up into N-sized chunks
The other answers show you how to "statically" partition the original array in different arrays using ArraySlice:s. Given your description, possibly you want to, generally, break up your original array into N-sized chunks (here: n = 5).
We could use the sequence(state:next) to implement such a chunk(bySize:) method as an extension to Collection:
extension Collection {
func chunk(bySize size: IndexDistance) -> [SubSequence] {
precondition(size > 0, "Chunk size must be a positive integer.")
return sequence(
state: (startIndex, index(startIndex, offsetBy: size, limitedBy: endIndex) ?? endIndex),
next: { indices in
guard indices.0 != self.endIndex else { return nil }
indices.1 = self.index(indices.0, offsetBy: size, limitedBy: self.endIndex) ?? self.endIndex
return (self[indices.0..<indices.1], indices.0 = indices.1).0
}).map { $0 }
}
}
Applied to your example:
var arrayElements = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]
let partitions = arrayElements.chunk(bySize: 5)
/* [["1", "2", "3", "4", "5"],
["6", "7", "8", "9", "10"],
["11"]] */
The chunk(bySize:) method will break up the array into bySize-sized chunks, as well as (possible) a smaller chunk for the final partition.
However, as much as I'd like to try to use the sequence(state:next) function (not needing to use any mutable intermediate variables other than state), the implementation above is quite bloated and difficult to read, so (as for so many other cases ...) we are probably better off simply using a while loop:
extension Collection {
func chunk(bySize size: IndexDistance) -> [SubSequence] {
precondition(size > 0, "Chunk size must be a positive integer.")
var chunks: [SubSequence] = []
var from = startIndex
while let to = index(from, offsetBy: size, limitedBy: endIndex) {
chunks.append(self[from..<to])
from = to
}
if from != endIndex { chunks.append(self[from..<endIndex]) }
return chunks
}
}
lol I don't see why there are so complicated answers here
(Consider the "array" variable as is -> [Int], not [Any])
So the first approach is just for Number types.
The second one should do it
Simply:
let array = [0,1,2,3,4,5,6,7,8,9,10]
//For instance..
var arrayA = ["A","B","C","D","E","F","G"]
//First 6 elements
let arrayOfFirstFour = array.filter({
return $0 <= 5 ? true : false
})
//Remaining elements:
let restOfArray = array.filter({
return $0 > 5 ? true : false
})
let elementsToFourth = arrayA.prefix(upTo: 4)
let elementsAfterFourth = arrayA.suffix(from: 4)
print(arrayOfFirstFour)
print(restOfArray)
print(elementsToFourth)
print(elementsAfterFourth)
//[0, 1, 2, 3, 4, 5]
//[6, 7, 8, 9, 10]
//["A", "B", "C", "D"]
//["E", "F", "G"]

Swift - Replace the 2nd character of a string

I have an array with a set of three-letter words that will be the foundation of a game I am working on. I am trying to replace the second character of all these words with an underscore in my UILabel. The user will have to tap the correct vowel to complete the word.
let games: [Game] = {
let firstGame = Game(question: "1", answer: "BAT", choice_1: "A", choice_2: "O", choice_3: "U", choice_4: "E", image: "_bat", audio: "bat.wav")
let secondGame = Game(question: "2", answer: "BIN", choice_1: "A", choice_2: "O", choice_3: "U", choice_4: "E", image: "_bin", audio: "bin.wav")
let thirdGame = Game(question: "3", answer: "BOX", choice_1: "A", choice_2: "O", choice_3: "U", choice_4: "E", image: "_box", audio: "box.wav")
return [firstGame, secondGame, thirdGame]
}()
I found the replacingOccurrences functionality, but this one only replaces a single letter or set. What is the easiest way to replace all my vowels with the _ character.
var game: Game? {
didSet {
questionLabel.text = "Question \(String(describing: game!.question)) of 10"
if (game?.image) != nil {
imageView.image = UIImage(named: (game?.image)!)
}
if let answerContent = game?.answer {
answerField.text = answerContent.replacingOccurrences(of: "A", with: "_")
answerField.addTextSpacing()
}
}
}
Replace vowels…
let vowels = CharacterSet(charactersIn: "AEIOU")
var string = "CAT"
if let range = string.rangeOfCharacter(from: vowels) {
string.replaceSubrange(range, with: "_")
}
print(string) // C_T
Replace second character…
let start = string.index(after: string.startIndex)
// Only 1 character, so using a closed range with start == end
let range = start...start
string.replaceSubrange(range, with: "_")
print(string) // C_T

Split string into substring with component separated by string swift

I have a string as shown below,
let newString : String = "12","34","56","78","910","1112","1314","1516","1718","1920","2122","2324","2526","2729".
i want to separate string with "4 string each" string e.g. "12","34","56","78" and "910","1112","1314","1516" and so on.
Can we achieve this by using range or something else?
Note :- newString is not static data it will come from webservice
You can try like this way, first create array from String, then make chunk array from it and then join the string from array.
let newString = "1,2,3,4,5,6,7,8,9,10,11,12"
let array = newString.components(separatedBy: ",") // ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
let chunkSize = 4
let chunksArray = stride(from: 0, to: array.count, by: chunkSize).map {
Array(array[$0..<min($0 + chunkSize, array.count)])
}
let subArray = chunksArray.map { $0.joined(separator: ",") }
// ["1,2,3,4", "5,6,7,8", "9,10,11,12"]
Edit: You can merge last two action with single like this way.
let subArray = stride(from: 0, to: array.count, by: chunkSize).map {
array[$0..<min($0 + chunkSize, array.count)].joined(separator: ",")
}
// ["1,2,3,4", "5,6,7,8", "9,10,11,12"]
First generate an array from string:
let newString = "1,2,3,4,1234,1235,1236,1238,678,679"
let newStringArray = newString.componentsSeparatedByString(",")
Then run for loop and add string using joinWithSeparator
You could split string the by , like: let strArr = newString.components(separatedBy: ","), then split the strArr to arrays containing 4 elements, and join each resulting array by ,. Hope this helps, good luck!

Hex String to Character in PURE Swift

I need a way to convert a string that contains a literal string representing a hexadecimal value into a Character corresponding to that particular hexadecimal value.
Ideally, something along these lines:
let hexString: String = "2C"
let char: Character = fromHexString(hexString)
println(char) // prints -> ","
I've tried to use the syntax: "\u{n}" where n is a Int or String and neither worked.
This could be used to loop over an array of hexStrings like so:
var hexArray = ["2F", "24", "40", "2A"]
var charArray = [Character]()
charArray = map(hexArray) { charArray.append(Character($0)) }
charArray.description // prints -> "[/, $, #, *]"
A couple of things about your code:
var charArray = [Character]()
charArray = map(hexArray) { charArray.append(Character($0)) }
You don't need to create an array and then assign the result of the map, you can just assign the result and avoid creating an unnecessary array.
charArray = map(hexArray) { charArray.append(Character($0)) }
Here you can use hexArray.map instead of map(hexArray), also when you use a map function what you are conceptually doing is mapping the elements of the receiver array to a new set of values and the result of the mapping is the new "mapped" array, which means that you don't need to do charArray.append inside the map closure.
Anyway, here is a working example:
let hexArray = ["2F", "24", "40", "2A"]
var charArray = hexArray.map { char -> Character in
let code = Int(strtoul(char, nil, 16))
return Character(UnicodeScalar(code))
}
println(charArray) // -> [/, $, #, *]
EDIT: This is another implementation that doesn't need Foundation:
func hexToScalar(char: String) -> UnicodeScalar {
var total = 0
for scalar in char.uppercaseString.unicodeScalars {
if !(scalar >= "A" && scalar <= "F" || scalar >= "0" && scalar <= "9") {
assertionFailure("Input is wrong")
}
if scalar >= "A" {
total = 16 * total + 10 + scalar.value - 65 /* 'A' */
} else {
total = 16 * total + scalar.value - 48 /* '0' */
}
}
return UnicodeScalar(total)
}
let hexArray = ["2F", "24", "40", "2A"]
var charArray = hexArray.map { Character(hexToScalar($0)) }
println(charArray)
EDIT2 Yet another option:
func hexToScalar(char: String) -> UnicodeScalar {
let map = [ "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9,
"A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15 ]
let total = reduce(char.uppercaseString.unicodeScalars, 0, { $0 * 16 + (map[String($1)] ?? 0xff) })
if total > 0xFF {
assertionFailure("Input char was wrong")
}
return UnicodeScalar(total)
}
Final edit: explanation
Given that the ascii table has all the number together (012345679), we can convert 'N' (base 10) to an integer knowing the ascii value of 0.
Because:
'0': 48
'1': 49
...
'9': 57
Then if for example you need to convert '9' to 9 you could do
asciiValue('9') - asciiValue('0') => 57 - 48 = 9
And you can do the same from 'A' to 'F':
'A': 65
'B': 66
...
'F': 70
Now we can do the same as before but, for example for 'F' we'd do:
asciiValue('F') - asciiValue('A') => 70 - 65 = 5
Note that we need to add 10 to this number to get the decimal. Then (going back to the code): If the scalar is between A-Z we need to do:
10 + asciiValue(<letter>) - asciiValue('A')
which is the same as: 10 + scalar.value - 65
And if it's between 0-9:
asciiValue(<letter>) - asciiValue('0')
which is the same as: scalar.value - 48
For example: '2F'
'2' is 2 and 'F' is 15 (by the previous example), right?. Since hex is base 16 we'd need to do:
((16 ^ 1) * 2) + ((16 ^ 0) * 15) = 47
Here you go:
var string = String(UnicodeScalar(Int("2C", radix: 16)!))
BTW, you can include hex values in the literal strings like this:
var string = "\u{2c}"
With Swift 5, you will have to convert your string variable into an integer (using init(_:radix:) initializer), create Unicode scalar from this integer (using init(_:)) then create a character from this Unicode scalar (using init(_:)).
The Swift 5 Playground sample code below shows how to proceed:
let validHexString: String = "2C"
let validUnicodeScalarValue = Int(validHexString, radix: 16)!
let validUnicodeScalar = Unicode.Scalar(validUnicodeScalarValue)!
let character = Character(validUnicodeScalar)
print(character) // prints: ","
If you want to perform this operation for the elements inside an array, you can use the sample code below:
let hexArray = ["2F", "24", "40", "2A"]
let characterArray = hexArray.map({ (hexString) -> Character in
let unicodeScalarValue = Int(hexString, radix: 16)!
let validUnicodeScalar = Unicode.Scalar(unicodeScalarValue)!
return Character(validUnicodeScalar)
})
print(characterArray) // prints: ["/", "$", "#", "*"]
Alternative with no force unwraps:
let hexArray = ["2F", "24", "40", "2A"]
let characterArray = hexArray.compactMap({ (hexString) -> Character? in
guard let unicodeScalarValue = Int(hexString, radix: 16),
let unicodeScalar = Unicode.Scalar(unicodeScalarValue) else {
return nil
}
return Character(unicodeScalar)
})
print(characterArray) // prints: ["/", "$", "#", "*"]
Another simple way based on ICU transforms:
extension String {
func transformingFromHex() -> String? {
return "&#x\(self);".applyingTransform(.toXMLHex, reverse: true)
}
}
Usage:
"2C".transformingFromHex()
Results in: ,