How to Sort the first dimension of a 2D Array Swift - 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
}

Related

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"

Swift Sort NSDictionary of NSDictionary by Value

I have a NSDictionary of NSDictionaries and I was wondering how do I sort the dictionaries by their value (Name)? The number will change from 0-n. Below is how the structure is setup:
let attribute = cumstomClass.Attribute!
attribute =
-Krqq2AWOqWKys0siTmc
Description: ""
Name: "B"
Value: ""
-KrqpSvSX7eKqYfvu5ZO
Description: ""
Name: "A"
Value: ""
Thanks in advance! :)
Since a Dictionary is inherently unsorted, you'll need to use an Array instead. Fortunately, this is easily done via functional techniques:
guard let dict = nsDict as? [String : [String : String]] else { /* handle the error */ }
let sorted = dict.sorted { ($0.value["Name"] ?? "") < ($1.value["Name"] ?? "") }
print(sorted)
This will get you an Array of tuples containing the keys and values of the respective dictionaries, sorted by "Name":
[(key: "KrqpSvSX7eKqYfvu5ZO", value: ["Name": "A", "Value": "", "Description": ""]),
(key: "Krqq2AWOqWKys0siTmc", value: ["Name": "B", "Value": "", "Description": ""])]
Alternately, you can simply get a sorted array of the String keys:
let sortedKeys = dict.sorted { ($0.value["Name"] ?? "") < ($1.value["Name"] ?? "") }.map {
$0.key
}
print(sortedKeys)
which outputs:
["KrqpSvSX7eKqYfvu5ZO", "Krqq2AWOqWKys0siTmc"]
This array of keys can later be used to dynamically look up objects from the dictionary at runtime when populating the collection view. This method will be likely to be more expensive performance-wise than the first approach due to all the dictionary lookups, but will result in lower memory usage if you also need to keep the original dictionary around in its original form for some reason.

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!

Generate section headers from dictionary [Swift 3]

I have an array of dictionaries with the following type of structure (which is already sorted) :
[
[
"id": 1,
"name": "ItemA",
"url": "http://url.com"
],
[
"id": 32,
"name": "ItemB",
"url": "http://url.com"
],
...
]
Declared as an array of dictionaries for AnyObject :
var arrayApps = [[String:AnyObject]]()
This array of dictionaries is generated using SwiftyJson :
[..]
if let resData = swiftyJsonVar["data"].arrayObject {
self.arrayItems = resData as! [[String:AnyObject]]
}
[..]
My Goal is to display those items in sections by using the sections headers but after trying to figure it out and looking for an answer, i'm unable to move on.
I've tried to groupe the dictionaries by letters to get a result like this:
[
"A":{[foo1],[foo2]},
"D":{[foo3],[foo5]},
"F":{[foo4],[foo6]}
...
]
But no luck, i've always ended up with errors because my array contains "Optionals".
In summary :
How can I generate Alphabetical section headers based on the name inside a TableView using an array of dictionaries not grouped like the one given above in Swift 3 ?
Thank you in advance !!
You can use the .sorted(by: ) method of Array to compare to elements of you array with each other.
This yields a sortedArray:
let sortedArray = arrayOfApps.sorted(by: {($0["name"] as! String) <= ($1["name"] as! String)})
This will crash if the itemName is not a String but I left it to you to handle any errors. For example changing it to:
$0["name"] as? String ?? ""
EDIT:
// Removed examples and added extension to create desired result
I found one of my old projects where I wrote such extension. Changed it a bit to suit your needs, tell me if it needs some change still:
extension Array {
func sectionTitlesForArray(withName name: (Element) -> String) -> Array<(title: String, elements: NSMutableArray)> {
var sectionTitles = Array<(title: String, elements: NSMutableArray)>()
self.forEach({ element in
var appended = false
sectionTitles.forEach({ title, elements in
if title == name(element) {
elements.add(element)
appended = true
}
})
if appended == false {
sectionTitles.append((title: name(element), elements: [element]))
}
})
return sectionTitles
}
}
// Usage single letter as Section title:
let sectionTitles = arrayOfApps.sectionTitlesForArray(withName: {
let name = $0["name"] as! String
return String(name[name.startIndex])
})
// Quick dirty pretty-print:
sectionTitles.forEach({ sectionTitle in
print("Section title: \(sectionTitle.title) \n")
sectionTitle.elements.forEach({ object in
let element = object as! Dictionary<String,Any>
print("Element name: \(element["name"]!)")
})
print("")
})

How to sort an array of Structures with/by dynamic property

Given an NSTableView that has an array of structures as its datasource. A user can click on any column heading to sort by that column. The column identifiers match the property names of the properties within the structure.
Given a structure
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
}
and an array of structures
var myArray = [MyStructure]()
The goal is that when a column heading is clicked, use that column's identifier to sort the array of structures by that column identifier/property
With an array of dictionaries, it was easy...
self.myArrayOfDictionaries.sortInPlace {
(dictOne, dictTwo) -> Bool in
let d1 = dictOne[colIdentifier]! as String;
let d2 = dictTwo[colIdentifier]! as String;
return d1 < d2 //or return d1 > d2 for reverse sort
}
The question is how to access the properties of the Structure dynamically, something like
let struct = myArray[10] as! MyStructure //get the 10th structure in the array
let value = struct["col0data"] as! String //get the value of the col0data property
If there is a better way, suggestions would be appreciated.
I should also note that the structure may have 50 properties so this is an effort to reduce the amount of code needed to sort the array by any one of those properties.
edit:
One solution is to change the structure to a class derived from NSObject. Then the properties could be accessed via .valueForKey("some key"). However, I am trying to keep this Swifty.
Maybe I have a solution to your problem. The advantage of this code over your solution is here you don't need to add a subscript method to your struct to create an hardcoded String-Property-Value map via code.
Here's my extension
extension _ArrayType {
func sortedBy(propertyName propertyName: String) -> [Self.Generator.Element] {
let mirrors = self.map { Mirror(reflecting: $0) }
let propertyValues = mirrors.map { $0.children.filter { $0.label == propertyName }.first?.value }
let castedValues = propertyValues.map { $0 as? String }
let sortedArray = zip(self, castedValues).sort { (left, right) -> Bool in
return left.1 < right.1
}.map { $0.0 }
return sortedArray
}
}
Usage
struct Animal {
var name: String
var type: String
}
let animals = [
Animal(name: "Jerry", type: "Mouse"),
Animal(name: "Tom", type: "Cat"),
Animal(name: "Sylvester", type: "Cat")
]
animals.sortedBy(propertyName: "name")
// [{name "Jerry", type "Mouse"}, {name "Sylvester", type "Cat"}, {name "Tom", type "Cat"}]
animals.sortedBy(propertyName: "type")
// [{name "Tom", type "Cat"}, {name "Sylvester", type "Cat"}, {name "Jerry", type "Mouse"}]
Limitations
The worst limitation of this solutions is that it works only for String properties. It can be change to work with any types of property by it must be at compile time. Right now I have not a solution to make it work with any king of property type without changing the code.
I already asked help for the core of the problem here.
I would definitely recommend simply embedding your dictionary into your struct. A dictionary is a much more suitable data structure for 50 key-value pairs than 50 properties – and you've said that this would be an acceptable solution.
Embedding the dictionary in your struct will give you the best of both worlds – you can easily encapsulate logic & you have have easy lookup of the values for each column ID.
You can now simply sort your array of structures like this:
struct MyStructure {
var dict = [String:String]()
init(col0Data:String, col1Data:String) {
dict["col0data"] = col0Data
dict["col1data"] = col1Data
}
}
var myArray = [MyStructure(col0Data: "foo", col1Data: "bar"), MyStructure(col0Data: "bar", col1Data: "foo")]
var column = "col0data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // [MyStructure(dict: ["col0data": "bar", "col1data": "foo"]), MyStructure(dict: ["col0data": "foo", "col1data": "bar"])]
column = "col1data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // MyStructure(dict: ["col0data": "foo", "col1data": "bar"])], [MyStructure(dict: ["col0data": "bar", "col1data": "foo"])
Here's an answer (but not the best answer); use subscripts to return the correct property, and set which property you are sorting by within the array.sort:
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
subscript(key: String) -> String? { //the key will be the col identifier
get {
if key == "col0data" {
return col0data
} else if key == "col1data" {
return col1data
}
return nil
}
}
}
And then here's how the sort works:
let identifier = the column identifier string,say col0data in this case
myArray.sortInPlace ({
let my0 = $0[identifier]! //the identifier from the table col header
let my1 = $1[identifier]!
return my0 < my1
})
If you do not know what types the values of MyStructure can be you will have a hard time comparing them to sort them. If you had a function that can compare all types you can have in MyStructure then something like this should work
struct OtherTypeNotComparable {
}
struct MyStructure {
var col0data = "cat" //name matches the column identifier
var col1data: OtherTypeNotComparable
}
let structures = [MyStructure(), MyStructure()]
let sortBy = "col1data"
func yourCompare(a: Any, b: Any) -> Bool {
return true
}
var expanded : [[(String, Any, MyStructure)]]
= structures.map { s in Mirror(reflecting: s).children.map { ($0!, $1, s) } }
expanded.sortInPlace { (a, b) -> Bool in
let aMatch = a.filter { $0.0 == sortBy }.first!.1
let bMatch = b.filter { $0.0 == sortBy }.first!.1
return yourCompare(aMatch, b: bMatch)
}
source: https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Mirror_Structure/index.html