Looping through a multidimensional array in swift - swift

So I am trying to iterate over an NSArray. My NSArray is an array of an array of strings. Here is a copy-paste of the first 1.5 elements
(
(
"Tater Tot Nachos",
"Fried Feta",
"The Ultimate Feta Bread",
"Cheese Bread",
"Aubrees Bread",
"The Wings!",
"Coconut Grove Chicken Sicks",
"Far East Wings",
"Bacon Brussels Sprouts"
),
(
"Shaved Brussels Sprout Salad",
"Greek Salad",
"Coronado Cobb Salad",
"Harvest Salad",
This is the function that's giving me the headache
func createMenu() {
if let list = cellDescripters {
for(index, item) in list.enumerated() {
for food in item {
//DO SOMETHING WITH "FOOD"
}
}
}
}
' cellDescripters ' Is a global variable and it is the array I was outlining at the top, basically an array of arrays of strings.
When I print the type of ' item ' I see it's of type __NSArrayM which is an NSMutableArray from my understanding. Looking at documentation NSMutableArrays are iterable.
However when I go to compile this code I get the error:
Type 'Any' does not conform to protocol 'Sequence'
Any help would be greatly appreciated.

I think following example give you help
for example i have array of string array like you =
[["beverages", "food", "suppliers"],["other stuff", "medicine"]];
var arrayExample = [["beverages", "food", "suppliers"],["other stuff", "medicine"]];
//Iterate array through for loop
for(index, item) in arrayExample.enumerated()
{
for food in item
{
print("index : \(index) item: \(food)")
}
}
OUTPUT
index : 0 item: beverages
index : 0 item: food
index : 0 item: suppliers
index : 1 item: other stuff
index : 1 item: medicine

Here's a more generic solution for iterating 2D arrays in Swift. Tested in Swift 4 on iOS 13.
Works only on Swift arrays, see the following link for converting your NSArray to Arrays: https://stackoverflow.com/a/40646875/1960938
// 2D array extension explanation: https://stackoverflow.com/a/44201792/1960938
fileprivate extension Array where Element : Collection, Element.Index == Int {
typealias InnerCollection = Element
typealias InnerElement = InnerCollection.Iterator.Element
func matrixIterator() -> AnyIterator<InnerElement> {
var outerIndex = self.startIndex
var innerIndex: Int?
return AnyIterator({
guard !self.isEmpty else { return nil }
var innerArray = self[outerIndex]
if !innerArray.isEmpty && innerIndex == nil {
innerIndex = innerArray.startIndex
}
// This loop makes sure to skip empty internal arrays
while innerArray.isEmpty || (innerIndex != nil && innerIndex! == innerArray.endIndex) {
outerIndex = self.index(after: outerIndex)
if outerIndex == self.endIndex { return nil }
innerArray = self[outerIndex]
innerIndex = innerArray.startIndex
}
let result = self[outerIndex][innerIndex!]
innerIndex = innerArray.index(after: innerIndex!)
return result
})
}
}
An example usage:
let sampleMatrix = [
["a", "b", "c"],
["d", "e"],
[],
["f"],
[]
]
// Should print: a, b, c, d, e, f
for element in sampleMatrix.matrixIterator() {
print(element)
}

Related

Repeat a specific item of an array

How to replace or repeat a specific item (v) of an array in Swift 3 / 4:
["A","s","B","v","C","s","D","v","E","s"]
to get this:
["A","s","B","v","v","C","s","D","v","v","E","s"]
or this:
["A","s","B","v","v","v","C","s","D","v","v","v","E","s"]
["A","s","B","v","v","v","v","C","s","D","v","v","v","v","E","s"]
The reason is that element v inserts pauses (sec) between audio files (A, B, C, ...). The number of repetitions of the item v should be set via a SegmentedControl (1,2, ..., 6).
Quick takeaway
extension Array where Element == String {
func repeatItem(_ item: Element, times n: Int) -> Array<Element> {
return flatMap { $0 == item ? Array(repeating: $0, count: n) : [$0] }
}
}
Detail explains
Use flatMap:
yourArray.flatMap { $0 == "v" ? [$0, $0] : [$0] }
Basically, this checks each element of the array. If it is "v", turn it into ["v", "v"]. If it is not "v", turn it into an array with that single element. Then it flattens all those arrays, hence flatMap.
You can also triple a specific item:
yourArray.flatMap { $0 == "v" ? [$0, $0, $0] : [$0] }
Or repeat it n times:
yourArray.flatMap { $0 == "v" ? Array(repeating: $0, count: n) : [$0] }
Use playground to verify it:
//: Playground - noun: a place where people can play
import Foundation
var inputArray = ["A","s","B","v","C","s","D","v","E","s"]
var expectArray2 = ["A","s","B","v","v","C","s","D","v","v","E","s"]
var expectArray3 = ["A","s","B","v","v","v","C","s","D","v","v","v","E","s"]
var expectArray4 = ["A","s","B","v","v","v","v","C","s","D","v","v","v","v","E","s"]
extension Array where Element == String {
func repeatItem(_ item: Element, times n: Int) -> Array<Element> {
return flatMap { $0 == item ? Array(repeating: $0, count: n) : [$0] }
}
}
print(inputArray.repeatItem("v", times: 2) == expectArray2)
print(inputArray.repeatItem("v", times: 3) == expectArray3)
print(inputArray.repeatItem("v", times: 4) == expectArray4)
You can use insert(:at:) using the specific index of an element.
var foo = [0,1,2,3,4,5,6,7]
foo.insert(0, at: foo[0])
Output
[0, 0, 1, 2, 3, 4, 5, 6, 7]
You can wrap this in a function to repeat as much as you need.
let array : [String] = ["A","s","B","v","C","s","D","v","E","s"]
print(replaceItem(array: array, item: "v"))
//Method
func replaceItem(array : [String], item : String) -> [String] {
var newAr: [String] = []
for arItem in array{
newAr.append(arItem)
if arItem == item {
newAr.append(arItem)
}
}
return newAr
}
output :
["A", "s", "B", "v", "v", "C", "s", "D", "v", "v", "E", "s"]

Geting object from array of array and it's array number

I'm using Swift 2.3 and I have the following type array of arrays of my custom object called Player
`var playing = [[obj-one, obj-two],[obj-three, obj-four]]`
How would I use a for-in loop or something else so I can get the array index and the object?
I have the following:
for (index, p) in playing { -- Expression type [[Player]] is ambigious
I've also tried
for in (index, p: Player) in playing { -- same result.
and
for in (index, p) in playing as! Player { -- doesn't conform to squence type
I want to just be able to print out which array the object belongs to and then work with that current object
Use enumerated() to pair up an index and an element, like this:
let a = [["hello", "world"], ["quick", "brown", "fox"]]
for outer in a.enumerated() {
for inner in outer.element.enumerated() {
print("array[\(outer.offset)][\(inner.offset)] = \(inner.element)")
}
}
This produces the following output:
array[0][0] = hello
array[0][1] = world
array[1][0] = quick
array[1][1] = brown
array[1][2] = fox
Functional approach:
let items = [["0, 0", "0, 1"], ["1, 0", "1, 1", "1, 2"]]
items.enumerated().forEach { (firstDimIndex, firstDimItem) in
firstDimItem.enumerated().forEach({ (secondDimIndex, secondDimItem) in
print("item: \(secondDimItem), is At Index: [\(firstDimIndex), \(secondDimIndex)]")
})
}
prints:
item: 0, 0, is At Index: [0, 0]
item: 0, 1, is At Index: [0, 1]
item: 1, 0, is At Index: [1, 0]
item: 1, 1, is At Index: [1, 1]
item: 1, 2, is At Index: [1, 2]
I wouldn't use a for loop, I would do something like this:
import Foundation
var playing = [["one", "two"], ["three", "four"]]
if let index = playing.index(where: { $0.contains("two") }) {
print(index)
} else {
print("Not found")
}
This prints:
0
Or to get the entire subarray containing what you want:
if let subarray = playing.first(where: { $0.contains("three") }) {
print(subarray)
} else {
print("Not found")
}
Prints:
["three", "four"]

Check if object is contained in Array [duplicate]

In Swift, how can I check if an element exists in an array? Xcode does not have any suggestions for contain, include, or has, and a quick search through the book turned up nothing. Any idea how to check for this? I know that there is a method find that returns the index number, but is there a method that returns a boolean like ruby's #include??
Example of what I need:
var elements = [1,2,3,4,5]
if elements.contains(5) {
//do something
}
Swift 2, 3, 4, 5:
let elements = [1, 2, 3, 4, 5]
if elements.contains(5) {
print("yes")
}
contains() is a protocol extension method of SequenceType (for sequences of Equatable elements) and not a global method as in
earlier releases.
Remarks:
This contains() method requires that the sequence elements
adopt the Equatable protocol, compare e.g. Andrews's answer.
If the sequence elements are instances of a NSObject subclass
then you have to override isEqual:, see NSObject subclass in Swift: hash vs hashValue, isEqual vs ==.
There is another – more general – contains() method which does not require the elements to be equatable and takes a predicate as an
argument, see e.g. Shorthand to test if an object exists in an array for Swift?.
Swift older versions:
let elements = [1,2,3,4,5]
if contains(elements, 5) {
println("yes")
}
For those who came here looking for a find and remove an object from an array:
Swift 1
if let index = find(itemList, item) {
itemList.removeAtIndex(index)
}
Swift 2
if let index = itemList.indexOf(item) {
itemList.removeAtIndex(index)
}
Swift 3, 4
if let index = itemList.index(of: item) {
itemList.remove(at: index)
}
Swift 5.2
if let index = itemList.firstIndex(of: item) {
itemList.remove(at: index)
}
Updated for Swift 2+
Note that as of Swift 3 (or even 2), the extension below is no longer necessary as the global contains function has been made into a pair of extension method on Array, which allow you to do either of:
let a = [ 1, 2, 3, 4 ]
a.contains(2) // => true, only usable if Element : Equatable
a.contains { $0 < 1 } // => false
Historical Answer for Swift 1:
Use this extension: (updated to Swift 5.2)
extension Array {
func contains<T>(obj: T) -> Bool where T: Equatable {
return !self.filter({$0 as? T == obj}).isEmpty
}
}
Use as:
array.contains(1)
If you are checking if an instance of a custom class or struct is contained in an array, you'll need to implement the Equatable protocol before you can use .contains(myObject).
For example:
struct Cup: Equatable {
let filled:Bool
}
static func ==(lhs:Cup, rhs:Cup) -> Bool { // Implement Equatable
return lhs.filled == rhs.filled
}
then you can do:
cupArray.contains(myCup)
Tip: The == override should be at the global level, not within your class/struct
I used filter.
let results = elements.filter { el in el == 5 }
if results.count > 0 {
// any matching items are in results
} else {
// not found
}
If you want, you can compress that to
if elements.filter({ el in el == 5 }).count > 0 {
}
Hope that helps.
Update for Swift 2
Hurray for default implementations!
if elements.contains(5) {
// any matching items are in results
} else {
// not found
}
(Swift 3)
Check if an element exists in an array (fulfilling some criteria), and if so, proceed working with the first such element
If the intent is:
To check whether an element exist in an array (/fulfils some boolean criteria, not necessarily equality testing),
And if so, proceed and work with the first such element,
Then an alternative to contains(_:) as blueprinted Sequence is to first(where:) of Sequence:
let elements = [1, 2, 3, 4, 5]
if let firstSuchElement = elements.first(where: { $0 == 4 }) {
print(firstSuchElement) // 4
// ...
}
In this contrived example, its usage might seem silly, but it's very useful if querying arrays of non-fundamental element types for existence of any elements fulfilling some condition. E.g.
struct Person {
let age: Int
let name: String
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
}
let persons = [Person(17, "Fred"), Person(16, "Susan"),
Person(19, "Hannah"), Person(18, "Sarah"),
Person(23, "Sam"), Person(18, "Jane")]
if let eligableDriver = persons.first(where: { $0.age >= 18 }) {
print("\(eligableDriver.name) can possibly drive the rental car in Sweden.")
// ...
} // Hannah can possibly drive the rental car in Sweden.
let daniel = Person(18, "Daniel")
if let sameAgeAsDaniel = persons.first(where: { $0.age == daniel.age }) {
print("\(sameAgeAsDaniel.name) is the same age as \(daniel.name).")
// ...
} // Sarah is the same age as Daniel.
Any chained operations using .filter { ... some condition }.first can favourably be replaced with first(where:). The latter shows intent better, and have performance advantages over possible non-lazy appliances of .filter, as these will pass the full array prior to extracting the (possible) first element passing the filter.
Check if an element exists in an array (fulfilling some criteria), and if so, remove the first such element
A comment below queries:
How can I remove the firstSuchElement from the array?
A similar use case to the one above is to remove the first element that fulfils a given predicate. To do so, the index(where:) method of Collection (which is readily available to array collection) may be used to find the index of the first element fulfilling the predicate, whereafter the index can be used with the remove(at:) method of Array to (possible; given that it exists) remove that element.
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let indexOfFirstSuchElement = elements.index(where: { $0 == "c" }) {
elements.remove(at: indexOfFirstSuchElement)
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
}
Or, if you'd like to remove the element from the array and work with, apply Optional:s map(_:) method to conditionally (for .some(...) return from index(where:)) use the result from index(where:) to remove and capture the removed element from the array (within an optional binding clause).
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let firstSuchElement = elements.index(where: { $0 == "c" })
.map({ elements.remove(at: $0) }) {
// if we enter here, the first such element have now been
// remove from the array
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
// and we may work with it
print(firstSuchElement) // c
}
Note that in the contrived example above the array members are simple value types (String instances), so using a predicate to find a given member is somewhat over-kill, as we might simply test for equality using the simpler index(of:) method as shown in #DogCoffee's answer. If applying the find-and-remove approach above to the Person example, however, using index(where:) with a predicate is appropriate (since we no longer test for equality but for fulfilling a supplied predicate).
An array that contains a property that equals to
yourArray.contains(where: {$0.propertyToCheck == value })
Returns boolean.
The simplest way to accomplish this is to use filter on the array.
let result = elements.filter { $0==5 }
result will have the found element if it exists and will be empty if the element does not exist. So simply checking if result is empty will tell you whether the element exists in the array. I would use the following:
if result.isEmpty {
// element does not exist in array
} else {
// element exists
}
Swift 4/5
Another way to achieve this is with the filter function
var elements = [1,2,3,4,5]
if let object = elements.filter({ $0 == 5 }).first {
print("found")
} else {
print("not found")
}
As of Swift 2.1 NSArrays have containsObjectthat can be used like so:
if myArray.containsObject(objectImCheckingFor){
//myArray has the objectImCheckingFor
}
Array
let elements = [1, 2, 3, 4, 5, 5]
Check elements presence
elements.contains(5) // true
Get elements index
elements.firstIndex(of: 5) // 4
elements.firstIndex(of: 10) // nil
Get element count
let results = elements.filter { element in element == 5 }
results.count // 2
Just in case anybody is trying to find if an indexPath is among the selected ones (like in a UICollectionView or UITableView cellForItemAtIndexPath functions):
var isSelectedItem = false
if let selectedIndexPaths = collectionView.indexPathsForSelectedItems() as? [NSIndexPath]{
if contains(selectedIndexPaths, indexPath) {
isSelectedItem = true
}
}
if user find particular array elements then use below code same as integer value.
var arrelemnts = ["sachin", "test", "test1", "test3"]
if arrelemnts.contains("test"){
print("found") }else{
print("not found") }
Here is my little extension I just wrote to check if my delegate array contains a delegate object or not (Swift 2). :) It Also works with value types like a charm.
extension Array
{
func containsObject(object: Any) -> Bool
{
if let anObject: AnyObject = object as? AnyObject
{
for obj in self
{
if let anObj: AnyObject = obj as? AnyObject
{
if anObj === anObject { return true }
}
}
}
return false
}
}
If you have an idea how to optimize this code, than just let me know.
Swift
If you are not using object then you can user this code for contains.
let elements = [ 10, 20, 30, 40, 50]
if elements.contains(50) {
print("true")
}
If you are using NSObject Class in swift. This variables is according to my requirement. you can modify for your requirement.
var cliectScreenList = [ATModelLeadInfo]()
var cliectScreenSelectedObject: ATModelLeadInfo!
This is for a same data type.
{ $0.user_id == cliectScreenSelectedObject.user_id }
If you want to AnyObject type.
{ "\($0.user_id)" == "\(cliectScreenSelectedObject.user_id)" }
Full condition
if cliectScreenSelected.contains( { $0.user_id == cliectScreenSelectedObject.user_id } ) == false {
cliectScreenSelected.append(cliectScreenSelectedObject)
print("Object Added")
} else {
print("Object already exists")
}
what about using a hash table for the job, like this?
first, creating a "hash map" generic function, extending the Sequence protocol.
extension Sequence where Element: Hashable {
func hashMap() -> [Element: Int] {
var dict: [Element: Int] = [:]
for (i, value) in self.enumerated() {
dict[value] = i
}
return dict
}
}
This extension will work as long as the items in the array conform to Hashable, like integers or strings, here is the usage...
let numbers = Array(0...50)
let hashMappedNumbers = numbers.hashMap()
let numToDetect = 35
let indexOfnumToDetect = hashMappedNumbers[numToDetect] // returns the index of the item and if all the elements in the array are different, it will work to get the index of the object!
print(indexOfnumToDetect) // prints 35
But for now, let's just focus in check if the element is in the array.
let numExists = indexOfnumToDetect != nil // if the key does not exist
means the number is not contained in the collection.
print(numExists) // prints true
Swift 4.2 +
You can easily verify your instance is an array or not by the following function.
func verifyIsObjectOfAnArray<T>(_ object: T) -> Bool {
if let _ = object as? [T] {
return true
}
return false
}
Even you can access it as follows. You will receive nil if the object wouldn't be an array.
func verifyIsObjectOfAnArray<T>(_ object: T) -> [T]? {
if let array = object as? [T] {
return array
}
return nil
}
You can add an extension for Array as such:
extension Array {
func contains<T>(_ object: T) -> Bool where T: Equatable {
!self.filter {$0 as? T == object }.isEmpty
}
}
This can be used as:
if myArray.contains(myItem) {
// code here
}

Group dictionary by key in Swift

I'm trying to implement a groupBy functionality where all the numbers of a nested list are grouped. My code so far:
struct MyClass {
var numbers: [Int]
...
}
var dict: [String : MyClass] = ...
let numbers = dict
.filter{ $0.0.containsString(searchString) }
.flatMap{ $0.1.numbers }
This yields me an Array of Ints. However I'd like to have a dictionary [Int : Int] with each unique number and the count of its occurence. So for example:
[1,2,3,4,1,2,2,1]
should be:
[1 : 2, 2 : 3, 3 : 1, 4 : 1]
I know there's a groupBy operator, but Swift doesn't seem to have one. I've tried with reduce:
func reducer(accumulator: [Int: Int], num: Int) -> [Int : Int] {
var acc = accumulator
acc[num]! += 1
return acc
}
filtered.reduce([:], combine: reducer)
But it crashes when I want to run it. Not sure why, I get a EXC_BAD_INSTRUCTION.
I'd appreciate any help.
let numbers = [1,2,3,4,1,2,2,1]
var results = [Int: Int]()
Set(numbers).forEach { number in results[number] = numbers.filter { $0 == number }.count }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
Actually I'm not very sure if this is what you want. I just looked at your examples.
Using NSCountedSet:
var objects = [1,2,3,4,1,2,2,1]
let uniques = NSCountedSet(array: objects)
uniques.forEach { results[$0 as! Int] = uniques.countForObject($0) }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
I would expect the crash to be ocurring on this line:
acc[num]! += 1
The first time this is called for a number, the entry doesn't exist in the dictionary yet so acc[num] is nil. Forcefully unwrapping it would cause a crash.
Not sure if this is the best solution but you can simple check for this case:
if (acc[num]) {
acc[num]! += 1
} else {
acc[num] = 1
}
Cleaner code from #vacawama in the comments:
acc[num] = (acc[num] ?? 0) + 1
Here's an extension to Array that does what you're asking:
extension Array where Element: Hashable {
var grouped: [Element:Int] {
var dict = [Element:Int]()
self.forEach { dict[$0] = (dict[$0] ?? 0) + 1 }
return dict
}
}
The key is the closure: { dict[$0] = (dict[$0] ?? 0) + 1 }.
It takes the current value in the array, tests to see if it's a key in the dictionary, returns the value for that key if it exists or 0 if it doesn't, then adds one and sets the key:value to be the pair of the current value and occurrences so far.
Example use:
[1,2,3,4,1,2,2,1].grouped // => [2: 3, 3: 1, 1: 3, 4: 1]
You need something like this:
if let _ = acc.indexForKey(num) {
acc[num]! += 1
}
else {
acc[num] = 1
}
It's sort of unclear what you're asking for, but here's a function that will take an array of ints and return a dictionary with the number as the key, and the count as the value:
func getDictionaryOfCounts(accumulator: [Int]) -> [Int : Int] {
var countingDictionary: [Int : Int] = [:]
accumulator.forEach { (value) in
if countingDictionary[value] != nil {
countingDictionary[value]! += 1
}
else{
countingDictionary[value] = 1
}
}
return countingDictionary
}

Sort Dictionary by values in Swift

Is there are analog of - (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator in swift?
How to do this without casting to NSDictionary?
I tried this, but it seems to be not a good solution.
var values = Array(dict.values)
values.sort({
$0 > $1
})
for number in values {
for (key, value) in dict {
if value == number {
println(key + " : \(value)");
dict.removeValueForKey(key);
break
}
}
}
Example:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
dict.sortedKeysByValues(>) // fanta (12), cola(10), sprite(8)
Just one line code to sort dictionary by Values in Swift 4, 4.2 and Swift 5:
let sortedByValueDictionary = myDictionary.sorted { $0.1 < $1.1 }
Try:
let dict = ["a":1, "c":3, "b":2]
extension Dictionary {
func sortedKeys(isOrderedBefore:(Key,Key) -> Bool) -> [Key] {
return Array(self.keys).sort(isOrderedBefore)
}
// Slower because of a lot of lookups, but probably takes less memory (this is equivalent to Pascals answer in an generic extension)
func sortedKeysByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
return sortedKeys {
isOrderedBefore(self[$0]!, self[$1]!)
}
}
// Faster because of no lookups, may take more memory because of duplicating contents
func keysSortedByValue(isOrderedBefore:(Value, Value) -> Bool) -> [Key] {
return Array(self)
.sort() {
let (_, lv) = $0
let (_, rv) = $1
return isOrderedBefore(lv, rv)
}
.map {
let (k, _) = $0
return k
}
}
}
dict.keysSortedByValue(<)
dict.keysSortedByValue(>)
Updated:
Updated to the new array syntax and sort semantics from beta 3. Note that I'm using sort and not sorted to minimize array copying. The code could be made more compact, by looking at the earlier version and replacing sort with sorted and fixing the KeyType[] to be [KeyType]
Updated to Swift 2.2:
Changed types from KeyType to Key and ValueType to Value. Used new sort builtin to Array instead of sort(Array) Note performance of all of these could be slightly improved by using sortInPlace instead of sort
You could use something like this perhaps:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
var myArr = Array(dict.keys)
var sortedKeys = sort(myArr) {
var obj1 = dict[$0] // get ob associated w/ key 1
var obj2 = dict[$1] // get ob associated w/ key 2
return obj1 > obj2
}
myArr // ["fanta", "cola", "sprite"]
This should give you the sorted keys based on value, and is a little more cleaner:
var sortedKeys = Array(dict.keys).sorted(by: { dict[$0]! < dict[$1]! })
I think this is the easiest way to sort Swift dictionary by value.
let dict = ["apple":1, "cake":3, "banana":2]
let byValue = {
(elem1:(key: String, val: Int), elem2:(key: String, val: Int))->Bool in
if elem1.val < elem2.val {
return true
} else {
return false
}
}
let sortedDict = dict.sort(byValue)
OneLiner :
let dict = ["b": 2, "a": 1, "c": 3]
(Array(dict).sorted { $0.1 < $1.1 }).forEach { (k,v) in print("\(k):\(v)") }
//Output: a:1, b:2, c:3
Swap out the .forEach with .map -> Functional programming
Syntactical sugar :
extension Dictionary where Value: Comparable {
var sortedByValue: [(Key, Value)] { return Array(self).sorted { $0.1 < $1.1} }
}
extension Dictionary where Key: Comparable {
var sortedByKey: [(Key, Value)] { return Array(self).sorted { $0.0 < $1.0 } }
}
["b": 2, "a": 1, "c": 3].sortedByKey // a:1, b:2, c:3
["b": 2, "a": 1, "c": 3].sortedByValue // a:1, b:2, c:3
Lots of answers, here's a one-liner. I like it because it makes full use of native Swift iterative functions and doesn't use variables. This should help the optimiser do its magic.
return dictionary.keys.sort({ $0 < $1 }).flatMap({ dictionary[$0] })
Note the use of flatMap, because subscripting a dictionary returns an optional value. In practice this should never return nil since we get the key from the dictionary itself. flatMap is there only to ensure that the result is not an array of optionals. If your array's associated value should BE an optional you can use map instead.
Sorting your keys by the dictionary's value is actually simpler than it appears at first:
let yourDict = ["One": "X", "Two": "B", "Three": "Z", "Four": "A"]
let sortedKeys = yourDict.keys.sort({ (firstKey, secondKey) -> Bool in
return yourDict[firstKey] < yourDict[secondKey]
})
And that's it! There's really nothing more to it. I have yet to find a quicker method, other than the same approach in form of a simple one-liner:
let yourDict = ["One": "X", "Two": "B", "Three": "Z", "Four": "A"]
let sortedKeys = yourDict.keys.sort { yourDict[$0] < yourDict[$1] }
Sorting a dictionary by key or value
Using Swift 5.2 internal handling of "sorted":
var unsortedDict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
// sorting by value
let sortedDictByValue = unsortedDict.sorted{ $0.value > $1.value } // from lowest to highest using ">"
print("sorted dict: \(sortedDictByValue)")
// result: "sorted dict: [(key: "fanta", value: 12), (key: "cola", value: 10), (key: "sprite", value: 8)]\n"
// highest value
print(sortedDictByValue.first!.key) // result: fanta
print(sortedDictByValue.first!.value) // result: 12
// lowest value
print(sortedDictByValue.last!.key) // result: sprite
print(sortedDictByValue.last!.value) // result: 8
// by index
print(sortedDictByValue[1].key) // result: cola
print(sortedDictByValue[1].value) // result: 10
// sorting by key
let sortedDictByKey = unsortedDict.sorted{ $0.key < $1.key } // in alphabetical order use "<"
// alternative:
// let sortedDictByKey = unsortedDict.sorted{ $0 < $1 } // without ".key"
print("sorted dict: \(sortedDictByKey)")
// result: "sorted dict: [(key: "cola", value: 10), (key: "fanta", value: 12), (key: "sprite", value: 8)]\n"
// highest value
print(sortedDictByKey.first!.key) // result: cola
print(sortedDictByKey.first!.value) // result: 10
// lowest value
print(sortedDictByKey.last!.key) // result: sprite
print(sortedDictByKey.last!.value) // result: 8
// by index
print(sortedDictByKey[1].key) // result: fanta
print(sortedDictByKey[1].value) // result: 12
The following might be useful if you want the output to be an array of key value pairs in the form of a tuple, sorted by value.
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let sortedArrByValue = dict.sorted{$0.1 > $1.1}
print(sortedArrByValue) // output [(key: "fanta", value: 12), (key: "cola", value: 10), (key: "sprite", value: 8)]
Since Swift 3.0 Dictionary has sorted(by:) function which returns an array of tuples ([(Key, Value)]).
let sorted = values.sorted(by: { (keyVal1, keyVal2) -> Bool in
keyVal1.value > keyVal2.value
})
Just cast it to NSDictionary and then call the method. Anywhere you use #selector in ObjC you can just use a String in Swift. So it would look like this:
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let sortedKeys = (dict as NSDictionary).keysSortedByValueUsingSelector("compare:")
or
let sortedKeys2 = (dict as NSDictionary).keysSortedByValueUsingComparator
{
($0 as NSNumber).compare($1 as NSNumber)
}
As of Swift 3, to sort your keys based on values, the below looks promising:
var keys = Array(dict.keys)
keys.sortInPlace { (o1, o2) -> Bool in
return dict[o1]! as! Int > dict[o2]! as! Int
}
var dict = ["cola" : 10, "fanta" : 12, "sprite" : 8]
let arr = dic.sort{ (d1,d2)-> Bool in
if d1.value > d2.value {
retrn true
}
}.map { (key,value) -> Int in
return value
}
Take look a clean implementation way.
print("arr is :(arr)")
The following way in Swift 3 sorted my dictionary by value in the ascending order:
for (k,v) in (Array(dict).sorted {$0.1 < $1.1}) {
print("\(k):\(v)")
}
SWIFT 3:
Using a few resources I put this beautifully short code together.
dictionary.keys.sorted{dictionary[$0]! < dictionary[$1]!}
This returns an array of the dictionary keys sorted by their values. It works perfectly & doesn't throw errors when the dictionary is empty. Try this code in a playground:
//: Playground - noun: a place where people can play
import UIKit
let dictionary = ["four": 4, "one": 1, "seven": 7, "two": 2, "three": 3]
let sortedDictionary = dictionary.keys.sorted{dictionary[$0]! < dictionary[$1]!}
print(sortedDictionary)
// ["one", "two", "three", "four", "seven"]
let emptyDictionary = [String: Int]()
let emptyDictionarySorted = emptyDictionary.keys.sorted{emptyDictionary[$0]! < emptyDictionary[$1]!}
print(emptyDictionarySorted)
// []
If you'd like some help on why the heck the code uses $0, $1 and doesn't even have parentheses after the "sorted" method, check out this post - https://stackoverflow.com/a/34785745/7107094
This is how I did it - sorting in this case by a key called position. Try this in a playground:
var result: [[String: AnyObject]] = []
result.append(["name" : "Ted", "position": 1])
result.append(["name" : "Bill", "position": 0])
result
result = sorted(result, positionSort)
func positionSort(dict1: [String: AnyObject], dict2: [String: AnyObject]) -> Bool {
let position1 = dict1["position"] as? Int ?? 0
let position2 = dict2["position"] as? Int ?? 0
return position1 < position2
}
Sorting the dictionary with a dictionary as the value (Nested dictionary)
var students: [String: [String: Any?]] = ["16CSB40" : ["Name": "Sunitha", "StudentId": "16CSB40", "Total": 90], "16CSB41" : ["Name": "Vijay", "StudentId": "16CSB40", "Total": 80], "16CSB42" : ["Name": "Tony", "StudentId": "16CSB42", "Total": 95]] // Sort this dictionary with total value
let sorted = students.sorted { (($0.1["Total"] as? Int) ?? 0) < (($1.1["Total"] as? Int) ?? 0) }
print(sorted) //Sorted result
Use this, and then just loop through the dictionary again using the output keys.
extension Dictionary where Value: Comparable {
func sortedKeysByValue() -> [Key] {
keys.sorted { return self[$0]! < self[$1]! }
}
}
...or this if you hate force unwrapping :)
extension Dictionary where Value: Comparable {
func sortedKeysByValue() -> [Key] {
keys.sorted { (key1, key2) -> Bool in
guard let val1 = self[key1] else { return true }
guard let val2 = self[key2] else { return true }
return val1 < val2
}
}
}