How to add element at Last index Swift - ios

I am getting an Array from server and I store it in NSMutableArray. Now the issue is that the Array is not sorted. For eg. array = ["A","B","None","C","D"]. I want to sort it and place the "None" element at last. i.e ["A","B","C","D","None"]. Tried swapping but was unable to match the condition, as the array may increase in future. Check my code below which is not working as expected.
if array.containsObject( "None" ){
print("\(array.indexOfObject("None"))")
let noneIndex = array.indexOfObject("None")
print(noneIndex)
array.removeObject(noneIndex)
print("Remove Array:-\(array)")
array.insertObject(noneIndex, atIndex: (array.lastObject?.index)!)
print("Sorted Array:-\(array)")
}

Maybe I'm misunderstanding what it is that you need to do, but you could use sorted() on your array if you just want to sort it alphabetically.
You could also use filter to remove "None" from your array, sort it, and then append "None" as the last element
For instance, if you have
let elements = ["Alpha", "Bold", "None", "charlie", "Delta", "echo", "zebra", "k"]
You could start out by filtering it:
let filteredElements = elements.filter { $0.uppercased() != "NONE"}
Sort the filtered elements:
var sortedElements = filteredElements.sorted { $0.uppercased() < $1.uppercased()}
Append "None"
sortedElements.append("None") // ["Alpha", "Bold", "charlie", "Delta", "echo", "k", "zebra", "None"]
And be done.
Here it is combined:
let lastElement = "None"
let elements = ["Alpha", "Bold", "None", "charlie", "Delta", "echo", "zebra", "k"]
var sortedElements = elements.filter({$0.uppercased() != lastElement.uppercased()}).sorted(by: {$0.uppercased() < $1.uppercased()})
sortedElements.append(lastElement)
Hope that helps you.

var array = ["A", "B", "None", "C", "D"]
if let noneIndex = array.index(of: "None") {
array.remove(at: noneIndex)
array.append("None")
}
print(array)

This should move None at the end of the array, and sort the other elements:
let ["A", "B", "None", "C", "D"]
array.sorted { $1 == "None" || $0 < $1 } // ["A", "B", "C", "D", "None"]
This simply takes benefits of the by argument that can be passed to the sort/sorted method from Array.
Edit #MartinR had a very strong point regarding the comparison predicate from this answer, which indeed doesn't offer a strong weak ordering. Sorting the array with a correct predicate would be along the lines of:
array.sorted { $0 == "None" ? false : $1 == "None" ? true : $0 < $1 }

This will work:
// starting test array
let array = ["B", "None", "C","P","None","A", "Q"]
var sorted = array.sorted { (str1, str2) -> Bool in
return str1 < str2
}
sorted.forEach { str in
if str == "None" {
if let idx = sorted.index(of: str) {
sorted.remove(at: idx)
sorted.append(str)
}
}
}
// Sorted array is now ["A", "B", "C", "P", "Q", "None", "None"]

Related

Custom string sorting in Swift

I have an array that I want sorted alphabetically (for the most part). For example I want an array of string to be sorted A-Z with the exception of elements starting with "g", I want elements starting with "g" to be last (or first if that's easier) in the array.
Example:
let list = ["apple", "car", "boat", "zebra", "ghost", "far"]
sorted should be:
["apple", "boat", "car", "far", "zebra", "ghost"]
How would one accomplish this?
You could use sorted(by:) and compare cases that start with "g" and then fallback to normal String comparison if that doesn't happen:
let sorted = list.sorted { a, b in
if a.first == "g" && b.first != "g" { return false }
if b.first == "g" && a.first != "g" { return true }
return a < b
}
I would split it into 2 arrays, sort each of them, then combine them again.
let list = ["apple", "car", "boat", "zebra", "ghost", "far"]
let listWithoutG = list.filter { !$0.hasPrefix("g") }
let listOnlyG = list.filter { $0.hasPrefix("g") }
let sorted = listWithoutG.sorted() + listOnlyG.sorted()
print("Sorted: \(sorted)")
Result:
Sorted: ["apple", "boat", "car", "far", "zebra", "ghost"]

How to sort a CSV string based on the inner values?

I have this CSV String
"B,C,D,A,E\n18945,12091,14058,2907,15132\n25,122,134,428,211"
which I have separated by \n and , and got below array:
I have this array:
[["B", "C", "D", "A", "E"], ["18945", "12091", "14058", "2907", "15132"], ["25", "122", "134", "428", "211"]]
where first array inside the main array is indicating the column names, which I need to sort alphabetically based on that the remaining arrays should also sorted.
Consider a table like this:
B -> 18945 -> 25
After sort, I am expecting an output like this:
[["A", "B", "C", "D", "E"], ["2907", "18945", "12091", "14058", "15132"], ["428", "25", "122", "134", "211"]]
IMO you should try to structure your data but if you really want to go through this path you just need to sort the first collection indices and map the collections using the sorted indices. Of course this assumes all collections have the same number of elements and the collection is not empty:
let table = [["B", "C", "D", "A", "E"], ["18945", "12091", "14058", "2907", "15132"], ["25", "122", "134", "428", "211"]]
let indices = table[0].indices.sorted { table[0][$0] < table[0][$1] }
let sorted = table.map { collection in
indices.map { collection[$0] }
}
If you want to be extra paranoid and make sure your code will never crash you can get the first collection using if let and use compact map checking if the collection contains each index before accessing them using subscript:
let table = [["B", "C", "D", "A", "E"], ["18945", "12091", "14058", "2907", "15132"], ["25", "122", "134", "428", "211"]]
if let columns = table.first {
let indices = columns.indices.sorted { columns[$0] < columns[$1] }
let sorted = table.map { collection in
indices.compactMap { collection.indices ~= $0 ? collection[$0] : nil }
}
print(sorted)
}
edit/update:
For a case insensitive sort:
let indices = columns.indices.sorted { columns[$0].caseInsensitiveCompare(columns[$1]) == .orderedAscending }
For a case and diacritic insensitive sort:
let indices = columns.indices.sorted { columns[$0].localizedStandardCompare(columns[$1]) == .orderedAscending }
This will print
[["A", "B", "C", "D", "E"], ["2907", "18945", "12091", "14058", "15132"], ["428", "25", "122", "134", "211"]]

Swift: Merge Array of dictionaries with duplicate key but unique values

I have a array of dictionaries which contains same key but different values. I want to merge these dictionaries and add all the values of same keys just like below:
var arrayofDict = [["2019":"A"],["2019":"B"],["2019":"C"],["2018":"A"],["2018":"c"],["2017":"A"],["2017":"B"],["2017":"C"],["2016":"A"],["2015":"A"],["2015":"B"]]
expected result as an Array like:
var newDict = [["2019":["A","B","C"]],["2018":["A","C"]],["2017":["A","B","C"]],["2016":["A"]],["2015":["A","B"]]]
This shows how to build a single dictionary. Your "expected result" is an array. Is this what you really expected or did you want an array?
You can iterate the dictionary items and build up the dictionary entries:
var arrayofDict = [["2019":"A"],["2019":"B"],["2019":"C"],["2018":"A"],["2018":"c"],["2017":"A"],["2017":"B"],["2017":"C"],["2016":"A"],["2015":"A"],["2015":"B"]]
var result = [String : [String]]()
for dict in arrayofDict {
for (key, value) in dict {
result[key, default: []].append(value)
}
}
print(result)
["2016": ["A"], "2018": ["A", "c"], "2015": ["A", "B"], "2019": ["A", "B", "C"], "2017": ["A", "B", "C"]]
Or, if you want an array:
let result2 = result.map { [$0.key: $0.value] }
print(result2)
[["2015": ["A", "B"]], ["2016": ["A"]], ["2019": ["A", "B", "C"]], ["2018": ["A", "c"]], ["2017": ["A", "B", "C"]]]
as #vacawama in more functional way
let result = arrayofDict.reduce(into: [String:[String]]()) { (acc, d) in
for key in d.keys {
// don't worry to force unwrap d[key], if key exist, the value is not nil
acc[key, default: []].append(d[key]!)
}
}

Swift How can I sort a list of clothing sizes (e.g. XL, S, 2XL, XXS)?

I've an array of dictionary with something like this:
[["XL":956], ["M":1010], ["S":998], ["L":955], ["XXL":921], ["XS":1041], ["30":45], ["28":41], ["32":46], ["26":35], ["34":50], ["One Size":1]]
How do I sort it so that it is in this order?
[["XS":1041], ["S":998], ["M":1010], ["L":955], ["XL":956], ["XXL":921], ["26":35], ["28":41], ["30":45], ["32":46], ["34":50], ["One Size":1]]
Note that every size is not always present, it's dynamic
If you're confident about knowing all of your size strings, and so happy to force unwrap, you can do it in a one-liner.
let order = ["XS", "S", "M", "L", "XL", "XXL", "26", "28", "30", "32", "34", "One Size"]
let sorted = sizes.sorted{order.firstIndex(of: $0.first!.key)! < order.firstIndex(of: $1.first!.key)!}
Depending on where you are going to use this, the better option would be to unwrap the optionals safely in case of bad data! :-)
Create a reference array that contains the alphabetical sizes in sorted order.
Now, sort the array based on reference array, i.e.
let data = [["XL":956], ["M":1010], ["S":998], ["L":955], ["XXL":921], ["XS":1041], ["30":45], ["28":41], ["32":46], ["26":35], ["34":50], ["One Size":1]]
let ref = ["XS", "S", "M", "L", "XL", "XXL"]
let result = data.sorted { (val1, val2) -> Bool in
if let key1 = val1.first?.key, let key2 = val2.first?.key {
if let x = Int(key1), let y = Int(key2) {
return x < y
} else if let x = ref.firstIndex(of: key1), let y = ref.firstIndex(of: key2) {
return x < y
} else {
return ref.firstIndex(of: key1) != nil
}
}
return false
}

Swift array map with comma before append new value

i have this following data structure in my Realm object
var tags = List<Tag>()
"tags": [
{
"tagId": "80069",
"tagName": "A"
},
{
"tagId": "80070",
"tagName": "B"
},
{
"tagId": "80071",
"tagName": "C"
},
{
"tagId": "80073",
"tagName": "D"
}
]
So what i want to achieve is, I map all my tag name into my new array
this is my code
let realmObject = self.realm.objects(MyDTO.self)
let array = Array(realmOutletList).map{Array($0.tags).map{$0.tagName!}.joined(separator: ",")}
it prints out this
["A,B,C", "A,C,D", "B,C,D"]
What I want to achieve is like
["A","B","C", "A","C","D", "B","C","D"]
I need that kind of array because I am going to create a Set from the array and then compare with another array
The compared array will be like
["A","B","C", "A","C","D", "B","C","D"]
because of the compared Array and the realmObject Array is different, it always shows false when i use
let subset = filterSet.isSubset(of: mySet)
Can anyone guide me please??
Thanks
Let's walk through solving the issue:
Consider that you have:
let originalArray = ["A,B,C", "A,C,D", "B,C,D"]
First, we need to separate each string in originalArray by "," character, so we could do:
let modifiedArray = originalArray.map { $0.components(separatedBy: ",") }
We map it to transform each string to a strings array (separation).
So far, the output of modifiedArray would be:
[["A", "B", "C"], ["A", "C", "D"], ["B", "C", "D"]]
which is an array of strings array.
Second, we need to spilt each -string- array in modifiedArray (having one reduced strings array instead), so we could do:
var final = [String]()
for array in modifiedArray {
for string in array {
final.append(string)
}
}
OR by using reduce
let finalArray = modifiedArray.reduce([], +)
Therefore, finalArray would be:
["A", "B", "C", "A", "C", "D", "B", "C", "D"]
which is the desired result.
Conclusion
For a fully one-lined answer (following the high-order functions approach):
let originalArray = ["A,B,C", "A,C,D", "B,C,D"]
let desiredArray = originalArray.map { $0.components(separatedBy: ",") }.reduce([], +)
print(desiredArray) // ["A", "B", "C", "A", "C", "D", "B", "C", "D"]
Well, it's quite easy:
This
let array = Array(realmOutletList).map{Array($0.tags).map{$0.tagName!}.joined(separator: ",")}
should be this
let array = Array(realmOutletList).flatMap{Array($0.tags).map{$0.tagName!}}
That's all. And you will get your ["A","B","C", "A","C","D", "B","C","D"].

Resources