substring with an array string - Swift - ios

I have an array:
var array = ["1|First", "2|Second", "3|Third"]
How I can cut off "1|", "2|", "3|"?
The result should look like this:
println(newarray) //["First", "Second", "Third"]

You can use (assuming the strings will contain the "|" character):
let newarray = array.map { $0.componentsSeparatedByString("|")[1] }
As #Grimxn pointed out, if you cannot assume that the "|" character will always be in the strings, use:
let newarray = array.map { $0.componentsSeparatedByString("|").last! }
or
let newarray2 = array.map { $0.substringFromIndex(advance(find($0, "|")!, 1)) }
result2 could be a little bit faster, because it doesn't create an intermediate array from componentsSeparatedByString.
or if you want to modify the original array:
for index in 0..<array.count {
array[index] = array[index].substringFromIndex(advance(find(array[index], "|")!, 1))
}

Related

Swift sorting nest String Array by Numbered Array Sort

I have a Nested string array, below it's just an example a nested string array, This nested array cant be sorted alphabetically, because of that I created a Numbered array that corresponds to each nested string array.
To be clear the nested array cannot be sorted alphabetically so dont provide answers regarding that.
What Im trying to do, But don't understand how to achieve is to sort my int array from smallest to largest.
From there or at the same time short the nest array based on the Int arrays index changed
Nested String Array Example (Array Size Is Dynamic)
[["ddd"],["aaa"],["ggg"]]
Int Array Example (Array Size Is Dynamic)
[557, 147, 355]
After Sorting Both would look like this
[["aaa"],["ggg"], ["ddd"]]
[147, 355, 557]
You can zip both arrays together and sort it by the second element of the tuple. Then you just need to map the first element:
let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]
let sorted = zip(a, b).sorted { $0.1 < $1.1 }
let sortedA = sorted.map(\.0)
sortedA // [["aaa"], ["ggg"], ["ddd"]]
let sortedB = sorted.map(\.1)
sortedB // [147, 355, 557]
You can get an index from one array and sort using values from the second array:
let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]
let sortedA = a.sorted { b[a.firstIndex(of: $0)!] < b[a.firstIndex(of: $1)!] }
// prints [["aaa"], ["ggg"], ["ddd"]]
let sortedB = b.sorted()
// prints [147, 355, 557]
This one-liner:
let sortedA = a.sorted { b[a.firstIndex(of: $0)!] < b[a.firstIndex(of: $1)!] }
is a shorter form of this:
let sortedA = a.sorted { item1, item2 in
let item1Index = a.firstIndex(of: item1)!
let item2Index = a.firstIndex(of: item2)!
return b[item1Index] < b[item2Index]
}
#pawello2222's solution works but it has n squared performance, meaning it will be very slow for large data-sets.
Optimized sort algorithms tend to have n * log(n) performance, which is much, much better.
The following code will use the native sorted method in Swift, which should have good performance:
let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]
let bIndexes = b
//turn the array b into a new array of tuples
//containing elements and "offsets" (indexes)
.enumerated()
.sorted { $0.element < $1.element } //Sort the new array by elements
.map { $0.offset } //Map the new array to just index.
print("bIndexes = \(bIndexes)")
//Loop through the array of indexes and use the indexes to fetch elements in sorted order.
bIndexes.forEach { print (b[$0], a[$0]) }
This outputs:
bIndexes = [1, 2, 0]
147 ["aaa"]
355 ["ggg"]
557 ["ddd"]
Saving the results into sorted arrays is a simple matter of mapping:
let newA = bIndexes.map { a[$0] }
let newB = bIndexes.map { b[$0] }
Edit:
Or better yet, use LeoDabus' solution:
let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]
let sortedIndexes = b.indices.sorted { b[$0] < b[$1] }
let sortedA = sortedIndexes.map { a[$0] }
let sortedB = sortedIndexes.map { b[$0] }
or if you only want the strings sorted based on the integer values:
let sortedA = b.indices.sorted { b[$0] < b[$1] }
.map { a[$0] }

How to split uncode string into characters

I have strings like
"\U0aac\U0ab9\U0ac1\U0ab5\U0a9a\U0aa8",
"\U0a97\U0ac1\U0ab8\U0acd\U0ab8\U0acb",
"\U0aa6\U0abe\U0ab5\U0acb",
"\U0a96\U0a82\U0aa1"
But I want to split this strings by unicode character
I dont know hot to do. I know components seprated by function but it's no use here.
\nAny help would be apperiaciated
If the strings you're getting really contain \U characters, you need to parse them manually and extract the unicode scalar values. Something like this:
let strings = [
"\\U0aac\\U0ab9\\U0ac1\\U0ab5\\U0a9a\\U0aa8",
"\\U0a97\\U0ac1\\U0ab8\\U0acd\\U0ab8\\U0acb",
"\\U0aa6\\U0abe\\U0ab5\\U0acb",
"\\U0a96\\U0a82\\U0aa1"
]
for str in strings {
let chars = str.components(separatedBy: "\\U")
var string = ""
for ch in chars {
if let val = Int(ch, radix: 16), let uni = Unicode.Scalar(val) {
string.unicodeScalars.append(uni)
}
}
print(string)
}
You can map your array, split its elements at non hexa digit values, compact map them into UInt32 values, initializate unicode scalars with them and map the resulting elements of your array into a UnicodeScalarView and init a new string with it:
let arr = [
#"\U0aac\U0ab9\U0ac1\U0ab5\U0a9a\U0aa8"#,
#"\U0a97\U0ac1\U0ab8\U0acd\U0ab8\U0acb"#,
#"\U0aa6\U0abe\U0ab5\U0acb"#,
#"\U0a96\U0a82\U0aa1"#]
let strings = arr.map {
$0.split { !$0.isHexDigit }
.compactMap { UInt32($0, radix: 16) }
.compactMap(Unicode.Scalar.init)
}.map { String(String.UnicodeScalarView($0)) }
print(strings)
This will print
["બહુવચન", "ગુસ્સો", "દાવો", "ખંડ"]
So, the string that comes back already has the "\" because in order to use components you'd need to have an additional escaping "\" so that you'd be able to do:
var listofCodes = ["\\U0aac\\U0ab9\\U0ac1\\U0ab5\\U0a9a\\U0aa8", "\\U0aac\\U0ab9\\U0ac1\\U0ab5\\U0a9a\\U0aa8"]
var unicodeArray :[String] = []
listofCodes.forEach { string in
unicodeArray
.append(contentsOf: string.components(separatedBy: "\\"))
unicodeArray.removeAll(where: {value in value == ""})
}
print(unicodeArray)
I will revise this answer once you specify how you are obtaining these strings, as is I get a non-valid string error from the start.

Swift search value for the nearest similar value reagrdless of sequence

Hi guys I want to ask how do you search the items for the nearest possible similar value regarding of the sequence. Example as below when I search for ["Restaurant","Bull"], it should return me str2 is the possible nearest values. Because this function only able to work for order sequence, it cannot for non-sequence. I really hope you guys can help me out....
func search(`for` searchItems: Set<String>, `in` searchArea: [Set<String>]) -> Set<String>? {
return searchArea.max(by: { (a, b) -> Bool in
return searchItems.intersection(a).count < searchItems.intersection(b).count || searchItems.intersection(a).count > searchItems.intersection(b).count
})
}
let str2: Set<String> = ["Bull","Restaurant","Corner"]
let str3: Set<String> = ["Corner","Restaurant","Mole"]
let area = [str3, str2] as [Any]
print("search result",self.search(for: ["Restaurant","Bull"], in: area as! [Set<String>]))
Probably because your str2 and str3 is not Set at all, it's array coz you are using array declaration, thus change it to this then it works if you use ["Bull", "Restaurant"]:
let str2 = Set(["Bull","Restaurant","Corner"])
let str3 = Set(["Corner","Restaurant","Mole"])
Also, Set is non-ordered sequence, Array is ordered sequence
Just make a set out of your query array and use its isSubset(of:) method to check wether it's a subset of your data.
let set1 = Set([1,2,3])
let set2 = Set([2,3,4])
let query = [1,2]
let querySet = Set(query)
querySet.isSubset(of: set1) // true
querySet.isSubset(of: set2) // false
let query2 = [2,1]
let querySet2 = Set(query)
querySet2.isSubset(of: set1) // true
querySet2.isSubset(of: set2) // false

How to parse json correctly in ios?

For example I need to get all english "text" values of "ex" scope from JSON example
What I do:
let result = json["def"].arrayValue.map({ $0["tr"].arrayValue.map { $0["ex"] } })
but at as a result I got a double massive and if I intend to get all "text" then I will get a triple array. Guess should be another more elegant approach to this task. Is anyone can show a really good solution?
If your expression gives 3 arrays of Strings you could add .reduce([], +) at the end to join the 3 arrays into
one.
EDIT:
I was typing from memory, and said the wrong thing. You would use reduce, not joined.
let result = json["def"]
.arrayValue
.map({ $0["tr"].arrayValue.map { $0["ex"] } })
.reduce([], +)
That should give you what you want.
EDIT #2:
The reduce function operates on sequences (like arrays). It takes an "initial result" value that seeds the process, and then a closure that operates on 2 elements, and returns a result. (a "binary" closure)
The + operator is actually a binary closure in Swift. It takes 2 elements and returns a single result, so you can simply pass in + instead of a closure. For arrays, + returns the result of combining the arrays.
So when you use reduce() on an array of arrays, and [] as the initial result, it combines [] with the first array, then the result of each + operator with the next entry in the array.
Take this simplified code for example:
//Start with an array of arrays of strings
let arrays = [
["string1", "string2", "string3"],
["string4", "string5", "string6"],
["string7", "string8", "string9"]
]
//First loop through the outer arrays and log their contents
for (index, object) in arrays.enumerated() {
print("array[\(index)] = \(object)")
}
//Now combine the outer arrays into a single array
let combined = arrays.reduce([], + )
//Now print the entries in the combined array
print("\n---- combined arrays ----")
for (index, object) in combined.enumerated() {
print("array[\(index)] = \(object)")
}
That produces the following output:
array[0] = ["string1", "string2", "string3"]
array[1] = ["string4", "string5", "string6"]
array[2] = ["string7", "string8", "string9"]
---- combined arrays ----
array[0] = string1
array[1] = string2
array[2] = string3
array[3] = string4
array[4] = string5
array[5] = string6
array[6] = string7
array[7] = string8
array[8] = string9

Parse CSV file in swift

I am parsing data from csv file to dictionary with the help of github.
After parsing I am getting this type of dictionary :-
{
"" = "";
"\"barred_date\"" = "\"\"";
"\"company_id\"" = "\"1\"";
"\"company_name\"" = "\"\"";
"\"contact_no\"" = "\"1234567890\"";
"\"created_date\"" = "\"2015-06-01 12:43:11\"";
"\"current_project\"" = "\"111\"";
"\"designation\"" = "\"Developer\"";
"\"doj\"" = "\"2015-06-01 00:00:00\"";
"\"fin_no\"" = "\"ABC001\"";
"\"first_name\"" = "\"sssd\"";
"\"last_name\"" = "\"dd\"";
"\"project_name\"" = "\"Project 1\"";
"\"qr_code\"" = "\"12345678\"";
"\"resignation_date\"" = "\"\"";
"\"status\"" = "\"1\"";
"\"work_permit_no\"" = "\"ssdda11\"";
"\"worker_id\"" = "\"1\"";
"\"worker_image\"" = "\"assets/uploads/workers/eb49364ca5c5d22f11db2e3c84ebfce6.jpeg\"";
"\"worker_image_thumb\"" = "\"assets/uploads/workers/thumbs/eb49364ca5c5d22f11db2e3c84ebfce6.jpeg\"";}
How can I convert this to simple dictionary. I need data like this "company_id" = "1"
Thanks
I recommend using CSVImporter – it takes care of things like quoted text (following RFC 4180) for you and even handles very large files without problems.
Compared to other solutions it works both asynchronously (prevents delays) and reads your CSV file line by line instead of loading the entire String into memory (prevents memory issues). On top of that it is easy to use and provides beautiful callbacks for indicating failure, progress, completion and even data mapping if you desire to.
You can use it like this to get an array of Strings per line:
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path)
importer.startImportingRecords { $0 }.onFinish { importedRecords in
for record in importedRecords {
// record is of type [String] and contains all data in a line
}
}
Take advantage of more sophisticated features like header structure support like this:
// given this CSV file content
firstName,lastName
Harry,Potter
Hermione,Granger
Ron,Weasley
// you can import data in Dictionary format
let path = "path/to/Hogwarts/students"
let importer = CSVImporter<[String: String]>(path: path)
importer.startImportingRecords(structure: { (headerValues) -> Void in
// use the header values CSVImporter has found if needed
print(headerValues) // => ["firstName", "lastName"]
}) { $0 }.onFinish { importedRecords in
for record in importedRecords {
// a record is now a Dictionary with the header values as keys
print(record) // => e.g. ["firstName": "Harry", "lastName": "Potter"]
print(record["firstName"]) // prints "Harry" on first, "Hermione" on second run
print(record["lastName"]) // prints "Potter" on first, "Granger" on second run
}
}
Use the CSwiftV parser instead: https://github.com/Daniel1of1/CSwiftV
It actually handles quoted text, and therefore it handles both line breaks and commas in text. SwiftCSV cost me some time in that it doesn't handle that. But I did learn about the CSV format and parsing it ;)
Parse CSV to two-dimension array of Strings (rows and columns)
func parseCsv(_ data: String) -> [[String]] {
// data: String = contents of a CSV file.
// Returns: [[String]] = two-dimension array [rows][columns].
// Data minimum two characters or fail.
if data.count < 2 {
return []
}
var a: [String] = [] // Array of columns.
var index: String.Index = data.startIndex
let maxIndex: String.Index = data.index(before: data.endIndex)
var q: Bool = false // "Are we in quotes?"
var result: [[String]] = []
var v: String = "" // Column value.
while index < data.endIndex {
if q { // In quotes.
if (data[index] == "\"") {
// Found quote; look ahead for another.
if index < maxIndex && data[data.index(after: index)] == "\"" {
// Found another quote means escaped.
// Increment and add to column value.
data.formIndex(after: &index)
v += String(data[index])
} else {
// Next character not a quote; last quote not escaped.
q = !q // Toggle "Are we in quotes?"
}
} else {
// Add character to column value.
v += String(data[index])
}
} else { // Not in quotes.
if data[index] == "\"" {
// Found quote.
q = !q // Toggle "Are we in quotes?"
} else if data[index] == "\r" || data[index] == "\r\n" {
// Reached end of line.
// Column and row complete.
a.append(v)
v = ""
result.append(a)
a = []
} else if data[index] == "," {
// Found comma; column complete.
a.append(v)
v = ""
} else {
// Add character to column value.
v += String(data[index])
}
}
if index == maxIndex {
// Reached end of data; flush.
if v.count > 0 || data[data.index(before: index)] == "," {
a.append(v)
}
if a.count > 0 {
result.append(a)
}
break
}
data.formIndex(after: &index) // Increment.
}
return result
}
Call above with the CSV data
let dataArray: [[String]] = parseCsv(yourStringOfCsvData)
Then extract the header row
let dataHeader = dataArray.removeFirst()
I assume you want an array of dictionaries (most spreadsheet data includes mulitple rows, not just one). The next loop is for that. But if you only need a single row (and header for keys) into a single dictionary, you can study below and get the idea of how to get there.
var da: [Dictionary<String, String>] = [] // Array of dictionaries.
for row in dataArray {
for (index, column) in row.enumerated() {
var d: Dictionary<String, String> = Dictionary()
d.updateValue(column, forKey: dataHeader[index])
da.append(d)
}
}

Resources