Split string into substring with component separated by string swift - 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!

Related

How to Sort the first dimension of a 2D Array Swift

Im trying to sort the columns of a CSV file,the contents of the CSV is provided in string
Beth,Charles,Danielle,Adam,Eric\n
17945,10091,10088,3907,10132\n
2,12,13,48,11
Converted String to 2D Array
[["Beth", "Charles", "Danielle", "Adam", "Eric"], ["17945", "10091", "10088", "3907",
"10132"], ["2", "12", "13", "48", "11"]]
How can i sort the only the first dimension of the 2D array or the Names in the 2D Array and still keep the mappings of the other dimension, i don't know how to explain this properly, but i hope the details below will help you understand what i want to achieve.
Adam,Beth,Charles,Danielle,Eric\n
3907,17945,10091,10088,10132\n
48,2,12,13,11
I want to achieve this with the names sorted and the other values in the other arrays mapping to the names like below,
[["Adam", "Beth", "Charles", "Danielle", "Eric"], ["3907", "17945", "10091", "10088",
"10132"], ["48", "2", "12", "13", "11"]]
Using this approach is not working but sorts the whole array
let sortedArray = 2dArray.sorted(by: {($0[0] as! String) < ($1[0] as! String) })
[["3907", "17945", "10091", "10088", "10132"], ["48", "2", "12", "13", "11"], ["Adam", "Beth", "Charles", "Danielle", "Eric"]]
Below if the full code
var stringCSV =
"Beth,Charles,Danielle,Adam,Eric\n17945
,10091,10088,3907,10132\n2,12,13,48,11";
var csvFormatted = [[String]]()
stringCSV.enumerateLines { line , _ in
var res = line.split(separator: ",",omittingEmptySubsequences:
false).map{ String($0) }
for i in 0 ..< res.count {
res[i] = res[i]
}
csvFormatted.append(res)
}
print(csvFormatted)
let sortedArray = csvFormatted.sorted(by: {($0[0] as! String)
< ($1[0] as! String) })
print(sortedArray)
Using "associated" arrays always ends up being messy.
I would start by creating a struct to represent each object (You haven't said what the numbers are, so I have picked a couple of property names. I have also kept String as their type, but converting to Int is possibly better, depending on what the data actually represents).
struct Person {
let name: String
let id: String
let age: String
}
Now you can combine the arrays and use that to build an array of these structs. Then you can sort by the name property.
let properties = zip(sourceArray[1],sourceArray[2])
let namesAndProperties = zip(sourceArray[0],properties)
let structArray = namesAndProperties.map { (name,properties) in
return Person(name: name, id: properties.0, age: properties.1)
}
let sortedArray = structArray.sorted {
return $0.name < $1.name
}

Get all values into dictionary and create a String with a specific format

I have a dictionary with this structure:
a: [1,2]
b: [3,4]
c: [5,6]
and I need to return a string with this structure.
a,b,c\n1,3,5\n2,4,6
I solved the first part of the string. But to get the rest of the String. I try to iterate into my dictionary to get the first elements for each key in my dictionary and then get the rest for each value into the array.
Is there an easier way to get this?
Once you know what's the order of the keys (alpha ?), you can use this:
let dict: [String: [Int]] = ["a": [1,2], "b": [3, 4], "c": [5, 6]]
let keys = dict.keys.sorted() //Or do whatever you want here to get your target order
var matrix: [[String]] = []
keys.forEach {
guard let arrayAsInt = dict[$0] else { return }
let arrayAsString = arrayAsInt.map{ "\($0)" }
matrix.append( [$0] + arrayAsString)
}
print("Matrix: \(matrix)")
let transposed = matrix.transposed()
print("Transposed Matrix: \(transposed)")
let output = transposed.map { $0.joined(separator: ",")}.joined(separator: "\n")
print(output)
The outputs:
$>Matrix: [["a", "1", "2"], ["b", "3", "4"], ["c", "5", "6"]]
$>Transposed Matrix: [["a", "b", "c"], ["1", "3", "5"], ["2", "4", "6"]]
$>a,b,c
1,3,5
2,4,6
Obvisouly the "\n" might be invisible and be an actual new line
a,b,c
1,3,5
2,4,6
Being
a,b,c\n1,3,5\n2,4,6
What's the idea behind that? Create a matrix and use the transpose (it's used in maths with matrix, it's one of the basic modification of a matrix).
First transform the [String: [Int]] into a [[String]], where each element would be key followed by its values. I transformed it there as String for simpler code later.
Why doing that? Because the matrix value is easy to get from your initial dict. the transposed value is harder (not impossible) to get from dict but easier from matrix, and the transposed is quickly transformed into your format.
So my thinking was the reverse:
Get a structure from your output, then how to get it, it's a transpose, so I need to get the initial input as it, etc.
With the help of a code for Transpose Matrix (that accept String elements).
extension Collection where Self.Iterator.Element: RandomAccessCollection {
// PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
func transposed() -> [[Self.Iterator.Element.Iterator.Element]] {
guard let firstRow = self.first else { return [] }
return firstRow.indices.map { index in
self.map{ $0[index] }
}
}
}
Any code (there a various) working ones, should the trick. I took it from here.
As pointed by #Leo Dabus, you can remove the Self.Iterator.Element
from the extension code (twice). I just wanted to it as such, not modifying the initial answer since it's not mind.
What you are looking for, besides composing the final string, is how to transpose a collection (this would work with collections of different sizes as well):
extension Sequence {
func max<T: Comparable>(_ predicate: (Element) -> T) -> Element? {
self.max(by: { predicate($0) < predicate($1) })
}
}
extension Collection where Element: RandomAccessCollection, Element.Indices == Range<Int> {
func transposed() -> [[Element.Element]] {
(0..<(max(\.count)?.count ?? .zero)).map {
index in compactMap { $0.indices ~= index ? $0[index] : nil }
}
}
}
let dict = ["a": [1,2,3],
"b": [4,5,6],
"c": [7,8,9]]
let sorted = dict.sorted(by: {$0.key < $1.key})
let result = sorted.map(\.key).joined(separator: ",") + "\n" +
sorted.map(\.value).transposed().map {
$0.map(String.init).joined(separator: ",")
}.joined(separator: "\n")
result // "a,b,c\n1,4,7\n2,5,8\n3,6,9"
A dictionary is an unordered collection so you need to sort it according to any specific key. Here I sort the dictionary according to the key if you don't care about an order you can just remove sort.
let dict: [String: Any] = ["a": [1,2], "b": [3,4], "c": [5,6]]
let sortedKey = dict.keys.sorted(by: <)
let key = sortedKey.joined(separator: ",")
var firstValues: [String] = []
var secondValues: [String] = []
sortedKey.forEach { (key) in
if let dictValue = dict[key] as? [Int],
let firstValue = dictValue.first,
let secondValue = dictValue.last {
firstValues.append("\(firstValue)")
secondValues.append("\(secondValue)")
}
}
let finalString = key + "\n" + firstValues.joined(separator: ",") + "\n" + secondValues.joined(separator: ",")
print(finalString) // "a,b,c\n1,3,5\n2,4,6"

How to return one single string from matching dictionary values?

I am trying to return one string of selected values from a dictionary. When I iterate over the dictionary and the array I am fetching the keys from, I can print out the value, but as different strings, not combined.
So far I have iterated over the collection types, and tried map, filter, reduce to some extent. Also tried to put the results into arrays and dictionaries to use .joined, but I am getting nil all the time.
This is the dictionary I am getting the values from:
let letterDict = ["a": "1", "b": "2", "c": "3", "d": "4"]
This is the array I am querying from:
let characterArray = ["a", "b"]
This is the code I was able to get the results from:
func convertText(_ input: String) -> String {
var expectedText: String?
for character in characterArray {
for (key, value) in letterDict {
if key.contains(character) {
print(value)
expectedText = value
}
}
}
return expectedText ?? input
}
I am trying to return 12, but what I get is:
1
2
all the time. Even when I try to map them to an array, it just returns separate arrays (or dictionaries). And when I try to append to an array, it just returns nil.
Thanks in advance.
You need to append value to expectedText rather than assign it.
func convertText(_ input: String) -> String {
var expectedText = ""
for character in characterArray {
for (key, value) in letterDict {
if key.contains(character) {
expectedText += value
}
}
}
return expectedText.isEmpty ? input : expectedText
}
Here's another way you might do it using compactMap with a [Character : Character] dictionary:
let letterDict: [Character : Character] = ["a": "1", "b": "2", "c": "3", "d": "4"]
let str = "ab"
var newstr = String(str.compactMap { letterDict[$0] })
print(newstr) // 12
This will return the empty string "" if no characters matched, whereas your code returned the original string. If that is the desired result, you could follow this with:
newstr = newstr.isEmpty ? str : newstr
So your function becomes:
func convertText(_ input: String) -> String {
let expectedText = String(input.compactMap { letterDict[$0] })
return expectedText.isEmpty ? input : expectedText
}
print(convertText("ab")) // 12
print(convertText("ef")) // ef

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