String into array in Swift 3 - arrays

I am trying to transform a string of the following format into an array (...of arrays, of floats!) in Swift 3:
"[173.0, 180.5],[173.0, 180.0],[174.0, 180.5],[174.0, 183.0]"
so that the output would be an array in this format:
[[173.0, 180.5, 173.0, 180.0],[174.0, 180.5, 174.0, 183.0]]
I am really new to Swift and struggling to find any String functions that will allow me to convert the data in this way. Any pointers on how I can do it would be awesome - thanks!

As Martin said, you first want to first convert this from a string to an array. In Swift 3:
let string = "[173.0, 180.5],[173.0, 180.0],[174.0, 180.5],[174.0, 183.0]"
let jsonString = "[" + string + "]"
guard let data = jsonString.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data),
let numberPairs = json as? [[Double]] else {
fatalError("string was not well-formed: \(string)")
}
You then want to combine these pairs of numbers together:
var combinedNumbers = [[Double]]()
var current: [Double]?
for numberPair in numberPairs {
if current != nil {
combinedNumbers.append(current! + numberPair)
current = nil
} else {
current = numberPair
}
}
// use `combinedNumbers` here
Clearly, you should use better variable names (perhaps something that suggests what these sets of numbers are), but hopefully this illustrates the idea.

Swift 4
You can use Decodable:
let str = "[173.0, 180.5],[173.0, 180.0],[174.0, 180.5],[174.0, 183.0]"
let json = "[\(str)]".data(using: .utf8)!
let numbers = try JSONDecoder().decode([[Double]].self, from: json).flatMap { $0 }
let result = stride(from: 0, to: numbers.count, by: 4).map { startIndex -> [Double] in
let endIndex = min(startIndex + 4, numbers.count)
return Array(numbers[startIndex..<endIndex])
}
Swift 3
One option is to use the old-school NSScanner to extract the numbers from the string to a flat array, then build a 2 dimensional array off that:
let str = "[173.0, 180.5],[173.0, 180.0],[174.0, 180.5],[174.0, 183.0]"
let scanner = Scanner(string: str)
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "[], ")
// Build the flat array
var numbers = [Double]()
while !scanner.isAtEnd {
var d = 0.0
if scanner.scanDouble(&d) {
numbers.append(d)
}
}
// Now the 2 dimensional array
let result = stride(from: 0, to: numbers.count, by: 4).map { startIndex -> [Double] in
let endIndex = min(startIndex + 4, numbers.count)
return Array(numbers[startIndex..<endIndex])
}

One option to convert the data types would be to develop a simple algorithm that will iterate through the string and analyze elements and square bracket delimiters, returning the appropriate conversion.
Below is the skeleton of what the fundamental components of such a function could look like.
Included are some basic aspects of the conversion from string to array.
var str = "[173.0, 180.5], [173.0, 180.0],[174.0, 180.5],[174.0, 183.0]"
// Cast returns array ["[","1","7","3",".","0",",".......]
let array = Array(str.characters)
// Iterate through array
for char in array {
if char == "[" || char == "]" {
// Deal with array delimiters appropriately
}
...
}
It might help to check out this similar problem.
NOTE: As Martin R mentioned, JSON interpretation may be a good method as well.

Related

Convert [MLMultiArray] to Float?

I have an MLMultiArray which is a result of an ML Model.
I need to convert it to Float so that I can further store it in Realm.
Below is an example of one of the MLMultiArray. The result from the ML Model contains 120 of the same vectors so its an array of MLMultiArrays i.e Array of Float32 1 x 128 matrices.
Float32 1 x 128 matrix
[4.476562,1.179688,0.07141113,6.976562,-0.2858887,-7.378906,0.6445312,3.695312,1.399414,2.486328,-3.988281,-0.2636719,1.000977,-4.480469,-7.832031,1.59082,0.8515625,-1.296875,-1.435547,7.839844,5.851562,0.3701172,-2.492188,7.273438,2.404297,-3.3125,-5.699219,-0.6816406,0.2807617,-3.882812,-3.982422,5.339844,4.125,-3.871094,0.6225586,1.712891,-10.02344,0.7119141,4.472656,3.566406,-0.559082,-1.049805,-4.679688,10.07812,-1.459961,4.707031,-6.078125,1.675781,-0.6259766,2.519531,3.472656,-3.400391,-6.714844,-4.933594,-1.733398,1.095703,-6.15625,9.234375,3.693359,-9.492188,0.8637695,0.8203125,-2.814453,-4.4375,-1.092773,3.332031,0.1623535,3.583984,-11.25781,-0.9941406,-0.3491211,1.464844,-1.579102,4.558594,2.703125,4.601562,5.914062,-2.402344,-5.46875,-0.355957,11.39062,2.070312,-7.289062,-0.4470215,-0.1595459,9.148438,1.833008,-2.097656,-3.9375,6.699219,-4.347656,-6.835938,-1.179688,3.910156,-13.07812,-1.947266,-0.9238281,-0.949707,-4.398438,2.363281,4.421875,4.632812,2.607422,8.773438,0.9106445,9.21875,-14.0625,-1.301758,-4.875,0.6054688,6.496094,-2.021484,3.898438,-4.644531,0.9853516,7.253906,3.066406,-1.051758,-8.09375,-6.527344,3.890625,5.175781,0.3701172,-0.5683594,-1.341797,0.1497803,4.074219,0.5932617]
Is there any way I can convert an array of MLMultiArray to Float32?
Any help would be appreciated <3
You can first convert the MLMultiArray to an UnsafeBufferPointer and then to a regular Array.
import CoreML
var a: [Float] = [ 1, 2, 3 ]
var m = try! MLMultiArray(a)
if let b = try? UnsafeBufferPointer<Float>(m) {
let c = Array(b)
print(c)
}
This is old question but this can help someone:
To convert from an MLMultiArray To An Array of primitive, You can use this function you will need just to change the output type
func convertToArray(from mlMultiArray: MLMultiArray) -> [Double] {
// Init our output array
var array: [Double] = []
// Get length
let length = mlMultiArray.count
// Set content of multi array to our out put array
for i in 0...length - 1 {
array.append(Double(truncating: mlMultiArray[[0,NSNumber(value: i)]]))
}
return array
}
To convert from an Array to MLMultiArray Use this, you may need to change the shape accordingly
func convertToMLMultiArray(from array: [Double]) -> MLMultiArray {
let length = NSNumber(value: array.count)
// Define shape of array
guard let mlMultiArray = try? MLMultiArray(shape:[1, length], dataType:MLMultiArrayDataType.double) else {
fatalError("Unexpected runtime error. MLMultiArray")
}
// Insert elements
for (index, element) in array.enumerated() {
mlMultiArray[index] = NSNumber(floatLiteral: element)
}
return mlMultiArray
}
I'm using this way, and subscript to access the element and it works fine for me.
const float *array = (float*)matrix.dataPointer

String Sorting Based on Last Character in Swift

I want to sort my string array based on last character. Here is my string array:
["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2",
"b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
Now I want to sort this array based on last value that is after last underscore(_).
Is it possible ?
Thanks
sorted can provide a custom sort condition for example (assuming that all strings are not empty)
let array = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2", "b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
let sortedArray = array.sorted { $0.substring(from: $0.index(before: $0.endIndex)) < $1.substring(from: $1.index(before: $1.endIndex)) }
Swift 3+ the syntax is much more convenient
let array = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2", "b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
let sortedArray = array.sorted { $0.suffix(1) < $1.suffix(1) }
No doubt it is. By using sorted(by:), you could do it like this:
let myArray = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2",
"b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
let sortedArray = myArray.sorted {
guard let str1LastChar = $0.characters.last, let str2LastChar = $1.characters.last else {
return false
}
return str1LastChar < str2LastChar
}
print(sortedArray)
Note that if myArray contains any empty string ("") the sort should be as is.
One more answer with Higher Order Function:
Reverse Each word in an Array
Then, Sort
let arr = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2",
"b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
One Line:
let returnValue = arr.map({String($0.reversed())}).sorted().map({String($0.reversed())})
Multi Line:
let reverseEachWordsArr = arr.map { value in
return String(value.reversed())
}
let finalCharSortArr = reverseEachWordsArr.sorted().map { word in
return String(word.reversed())
}
print(finalCharSortArr)
OUTPUT:
["b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1",
"c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2"]

Convert string of coordinates into an array of CLLocations in Swift

I have a string with a list of coordinates that I need to convert into an array. I tried to do let array = Array(coordinates) but it says String no longer conforms to SequenceType. I need to convert the string to an array of CLLocations. The string I am trying to convert looks like this:
let coordinates = "[[39.86475483576405,-75.53281903266907], [39.864688955564304,-75.53292632102966], [39.86455719497505,-75.53300142288208], [39.86440072894666,-75.5330228805542], [39.8642689678039,-75.53295850753784], [39.863305456757146,-75.53223967552185], [39.86303369478483,-75.53266882896423]]"
The string is a valid JSON string.
The most straightforward way is to deserialize the string with JSONSerialization and map the result to [CLLocation]
let coordinates = "[[39.86475483576405,-75.53281903266907], [39.864688955564304,-75.53292632102966], [39.86455719497505,-75.53300142288208], [39.86440072894666,-75.5330228805542], [39.8642689678039,-75.53295850753784], [39.863305456757146,-75.53223967552185], [39.86303369478483,-75.53266882896423]]"
if let data = coordinates.data(using: .utf8),
let jsonArray = try? JSONSerialization.jsonObject(with: data) as? [[Double]] {
let locationArray = jsonArray!.map{CLLocation(latitude:$0[0], longitude:$0[1]) }
print(locationArray)
}
One approach would be to strip all of the brackets and spaces and then split the comma separated numbers into an array. Then convert each pair of number strings into numbers and finally create a CLLocation from the pair of numbers.
// Your string
let coordinates = "[[39.86475483576405,-75.53281903266907], [39.864688955564304,-75.53292632102966], [39.86455719497505,-75.53300142288208], [39.86440072894666,-75.5330228805542], [39.8642689678039,-75.53295850753784], [39.863305456757146,-75.53223967552185], [39.86303369478483,-75.53266882896423]]"
// Remove the brackets and spaces
let clean = coordinates.replacingOccurrences(of: "[\\[\\] ]", with: "", options: .regularExpression, range: nil)
// Split the comma separated strings into an array
let values = clean.components(separatedBy: ",")
var coords = [CLLocation]()
for i in stride(from: 0, to: values.count, by: 2) {
// Pull out each pair and convert to Doubles
if let lat = Double(values[i]), let long = Double(values[i+1]) {
let coord = CLLocation(latitude: lat, longitude: long)
coords.append(coord)
}
}

Swift Convert A String-As-An-Array, To An Array Of Strings

I have a string:
"["word1","word2"]"
And I want a simple way to convert it to an actual [String].
All the other questions I could dig up on there were about converting int strings to arrays.
I tried doing
Array(arrayLiteral: "["word1","word2"]")
But I get
["[\"word1\",\"word2\"]"]
Manually cleaning up the edges and removing the slashes seems like I'm doing something very wrong.
I'm curious if there's a simple way to convert a an array of strings as a string into an array of strings.
i.e. Convert "["word1","word2"]" to ["word1","word2"]
Solution (Thanks to #Eric D)
let data = stringArrayString.dataUsingEncoding(NSUTF8StringEncoding)
var stringsArray:[String]!
do
{
stringsArray = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String]
} catch
{
print()
}
print("Array is \(stringsArray)")
Encode your "string array" to data, then decode this data as JSON to a Swift Array.
Like this, for example:
let source = "[\"word1\",\"word2\"]"
guard let data = source.dataUsingEncoding(NSUTF8StringEncoding),
arrayOfStrings = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String] else {
fatalError()
}
print(arrayOfStrings) // ["word1", "word2"]
print(arrayOfStrings[1]) // "word2"
Agree with comments above, I'd probably use a JSON parser. Failing that (or if you can't for some reason), I do not know of any built-in way; you'd have to do it manually. I'd do something like:
extension String {
func stringByTrimmingFirstAndLast() -> String {
let startIndex = self.startIndex.successor()
let endIndex = self.endIndex.predecessor()
return self.substringWithRange( startIndex..<endIndex )
}
}
let str = "[\"word1\",\"word2\"]"
let array = str
.stringByTrimmingFirstAndLast()
.componentsSeparatedByString(",")
.map { string in
return string.stringByTrimmingFirstAndLast()
}

Swift code: how do you build a Dictionary of Array of String based on Dictionary entry being nil

I have been trying to understand how to do some simple things with Swift using Dictionary and Array types.
I started by trying to write the simple functions below for manipulating a list of strings and bucketing them into the same array when the strings are anagrams of one another..!
Everything here should run in a playground on Xcode.
Questions:
Why can I not use toString() to convert Character Array to a String. I have to iterate over the Array of Character and build the string using += ?
What is the best way to add an Array to the Dictionary in the fly as I find that the Dictionary is empty for that string index so I need to create a new Array to hold the strings that sage the same letters. The confusion about a Dictionary not holding the types you think but actually holding Type? (that is Option Type - in my case String?, optional String).
See the crazy code below.
groupAnagrams(
listofWords: pass an array of String (immutable)
)
returns
Dictionary of Array of String (grouped anagrams)
A summary of numbers of actions taken in a Array of String
A log of actions taken in an Array of String
Purpose
Group the words that are anagrams together into buckets
The func will create a Dictonary indexed on a String that is the order string of characters, e.g.
["bats", "stab"] => [ "abst" : ["rats", "stab"] ]
Function charArrayToString
func charArrayToString(charArray ca: [Character]) -> String {
var s = ""
for c in ca {
s += String(c)
}
return s
}
func groupAnagrams(#listOfWords: [String]) -> [String:[String]] {
var s = ""
var d: [String:[String]] = [String:[String]]()
var i = 0, j = 0, k = 0
var log = [String]()
for s in listOfWords {
var s1:[Character] = [Character](s)
// var s1 = s
sort(&s1) { (a: Character, b: Character) -> Bool in return String(a) < String(b) }
//??? var s2 = toString(s1) // converts the Array of Character to a string (but it's for the for "[a,b,c,d]" and not "abcd" as you'd expect!
var s3 = charArrayToString(charArray: s1)
// Array already exists, add a string ELSE create the String array [String]() and add the fisst element
if let p = d[s3] {
// Array exists but d[s3] is "immutable" as it's optional array of String (type [String]?)
++i
var arr = d[s3]!
arr += [s]
d[s3] = arr
log += "Add string \(arr.count) \(s) to array \(s3) \n"
} else {
// ELSE: Create new String array
++j
d[s3] = [String]()
var arr = d[s3]!
arr += [s]
d[s3] = arr
log += "Create array for \(s3) with string \(s) \n"
}
++k
// break
}
var summary = [String]()
summary += ["\(i) strings added to already created arrays"]
summary += ["\(j) arrays created"]
summary += ["\(k) words processed"]
summary[0] // inspect
summary[1] // inspect
summary[2] // inspect
log // inspect
return d
}
// Same as the array version of the function but just adds stings to the paratner list of the function
func groupAnagramsList(strings: String...) -> [String:[String]] {
return groupAnagrams(listOfWords: strings)
}
Calls to the various functions created:
var listOfWords = [String]() + ["bats", "star", "tree", "stab", "rats", "reet", "pong", "peel", "leep", "path", "type", "pyte", "ypte"]
var anagrams = groupAnagrams(listOfWords: listOfWords)
var otherWay = groupAnagramsList("bats", "star", "tree", "stab", "rats", "reet", "pong", "peel", "leep", "path", "type", "pyte", "ypte")
UPDATE FROM Swift Beta 5:
if d[s3] == nil {
d[s3] = [String]()
++j
} else {
++i
}
d[s3]! += [s]
log += ["Add string \(d[s3]!.count) \(s) to array \(s3) \n"]
The check for nil on the Dictionary d is valid! In this context the complicated optional / immutable behavior is gone and the code is much easier to understand.
Note: I had to change
log += "Add string (d[s3]!.count) (s) to array (s3) \n"
To:
log += ["Add string (d[s3]!.count) (s) to array (s3) \n"]
Because you now need to explicitly add an Array of String to an Array of String. You cannot assume that += on a String will append a String to the Array. But it's easy, just wrap the string in '[' and ']'.
I still don't know why I cannot just cast a Character Array to a String!
Maybe a smart thing would be to use Swift's built-in Objective-C APIs to eliminate duplicate values. E.g:
let unique = NSSet(array: swiftArrayOfNonUniqueStrings).allObjects
The problem is, applying this to your character-sorted, duplicates-containing string array will just return the character-sorted version of your original string. (You can obtain such an array by returning alphaList below.)
As for sorting the characters within the string, I believe that iterating through the characters to build the string is efficient enough. You can write this a bit more succinctly
func alphabetizeStrings(list : [String]) -> [String] {
var newlist = [String]()
var alphaList = [String]()
for s in list {
var charArray = [Character](s)
charArray.sort({ (a: Character, b: Character) -> Bool in return String(a) < String(b)})
var sortedString = ""
for c in charArray {
sortedString += c
}
if NSArray(array: alphaList).containsObject(sortedString) == false {
newlist.append(s)
}
alphaList.append(sortedString)
}
return newlist
}
Thus:
let swiftarray = ["foo", "foo", "bar", "bra", "bat"]
let unique = alphabetizeStrings(swiftarray)
// ["foo", "bar", "bat"]

Resources