NSDictionary find keyPath - ios

I have a random dictionary, I want to iterate over all objects that are inside that dictionary. Is there a way to find keyPath of object that is inside the dictionary?
Let's say we have this dictionary .
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"GlossSee": "markup"
}
}
}
}
}
Now I want to find keyPath of GlossEntry or any other object.

Consider:
You can obtain all the keys in a dictionary and iterate over them. E.g. for (NSString *key in dictionary) {...}
Given a key you can obtain the matching value and test whether it is itself a dictionary.
Recursion is your friend. You could write a function which takes a current key path prefix, a dictionary, and a mutable array to add found key paths to. The implementation of this would involve (1), (2) and a recursive call.
Now write yourself some code. If you get stuck ask a new question, include your code, and explain where you are stuck.
HTH

Related

OpenAPI 3 Dictionary Define Key & Value Types

I have read https://swagger.io/docs/specification/data-models/dictionaries/, but am still having trouble modeling something like:
{
"top": {
"elem0": {
...
},
"elem1": {
...
},
...
}
}
top is a dictionary, containing an arbitrary number of entries. The trouble is that I want to define a reusable schema for the key and value types.
E.g., instead of saying that the keys are of type string, I say they are of type #/components/schemas/KEY. And instead of saying that the values are of type object, I say they are of type #/components/schemas/VALUE.
Is this possible?

Get a value from a dictionary given the key of a different field

I'm using the third-party library SwiftAddressBook to work with Contacts in my app. I want to iterate through contacts in my device and extract a few specific values. Namely the twitter and facebook usernames if they exist.
SwiftAddressBook has an object called SwiftAddressBookPerson which represents a single contact. And SwiftAddressBookPerson object has a property called socialProfiles which contains an array of dictionaries. Each dictionary contains info about available social media account such as the username, url etc. Printing the value of socialProfiles looks like this.
[
SwiftAddressBook.MultivalueEntry<Swift.Dictionary<SwiftAddressBook.SwiftAddressBookSocialProfileProperty, Swift.String>>(value: [
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://twitter.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "twitter"],
label: nil, id: 0),
SwiftAddressBook.MultivalueEntry<Swift.Dictionary<SwiftAddressBook.SwiftAddressBookSocialProfileProperty, Swift.String>>(value: [
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://www.facebook.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "facebook"],
label: Optional("facebook"), id: 1)
]
I cleaned it up a little by doing this socialProfiles.map { $0.map { $0.value } } which outputs the following.
[
[
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://twitter.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "twitter"
],
[
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.url: "http://www.facebook.com/isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username: "isuru",
SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service: "facebook"
]
]
What I want now is given the service's name (twitter, facebook), retrieve the username used for that service.
How do I parse through the dictionary and get the value of a different field?
Given that this is an array, I'd use a first { $0.service == serviceName }.
Alternatively, you could loop over the entire array ahead of time to create a dictionary with key as serviceName, and value as the profile property. The advantage of this approach is subsequent lookups would be much faster.
I have named your array of swift address book dictionaries, profiles. The forced unwrapping in the flatMap call is safe because both key and value are tested for non-nil in the previous filter call.
The result is a single dictionary containing username values for the services listed in your interestingServices array.
let service = SwiftAddressBook.SwiftAddressBookSocialProfileProperty.service
let username = SwiftAddressBook.SwiftAddressBookSocialProfileProperty.username
let interestingServices = ["facebook", "twitter"]
let usernames = profiles
.filter {
guard
let service = $0[service],
$0[username] != nil
else {
return false
}
return interestingServices.contains(service)
}
.flatMap { profile in
return [profile[service]!, profile[username]!]
}

How to map ordered NSDictionary using JSONModel

I've got a JSON as below.
odds: {
0501: {
x: 2.75,
description: "a"
},
0502: {
x: 3.25,
description: "b"
},
0513: {
x: 3.5,
description: "c"
},
0503: {
x: 3.5,
description: "d"
},
0505: {
x: 7.5,
description: "e"
},
0504: {
x: 7.5,
description: "f"
},
0512: {
x: 10,
description: "g"
}
}
This hash comes from HTTP response as I want to show but the thing that I use JSONModel to map it and there is only way to map that NSDictionary. When map this JSON to NSDictionary (as you can guess) this an unordered and sequence of data comes up mixed.
So, how to map this JSON, without broke up its sequence using JSONModel and NSDictionary ?
NSDictionary is inherently unordered:
Are keys and values in an NSDictionary ordered?
If you want to preserve the order of key-value entries, you need to use a data structure other than NSDictionary. Any library that passes your data through an NSDictionary cannot preserve the order.
Something I've done in this situation is to sort the dictionary keys in a separate array, in addition to the dictionary. Use the ordered key array to determine how to display your dictionary values.
Dictionaries can not be sorted, but as your JSON seems to be an array of objects, iterate thru your resulting NSDictionary with for...in and add the elements to a mutable array.
Afterwards sort the resulting array using .sortInPlace by comparing the x-value.

Ordered map in Swift

Is there any built-in way to create an ordered map in Swift 2? Arrays [T] are sorted by the order that objects are appended to it, but dictionaries [K : V] aren't ordered.
For example
var myArray: [String] = []
myArray.append("val1")
myArray.append("val2")
myArray.append("val3")
//will always print "val1, val2, val3"
print(myArray)
var myDictionary: [String : String] = [:]
myDictionary["key1"] = "val1"
myDictionary["key2"] = "val2"
myDictionary["key3"] = "val3"
//Will print "[key1: val1, key3: val3, key2: val2]"
//instead of "[key1: val1, key2: val2, key3: val3]"
print(myDictionary)
Are there any built-in ways to create an ordered key : value map that is ordered in the same way that an array is, or will I have to create my own class?
I would like to avoid creating my own class if at all possible, because whatever is included by Swift would most likely be more efficient.
You can order them by having keys with type Int.
var myDictionary: [Int: [String: String]]?
or
var myDictionary: [Int: (String, String)]?
I recommend the first one since it is a more common format (JSON for example).
Just use an array of tuples instead. Sort by whatever you like. All "built-in".
var array = [(name: String, value: String)]()
// add elements
array.sort() { $0.name < $1.name }
// or
array.sort() { $0.0 < $1.0 }
"If you need an ordered collection of key-value pairs and don’t need the fast key lookup that Dictionary provides, see the DictionaryLiteral type for an alternative." - https://developer.apple.com/reference/swift/dictionary
You can use KeyValuePairs,
from documentation:
Use a KeyValuePairs instance when you need an ordered collection of key-value pairs and don’t require the fast key lookup that the Dictionary type provides.
let pairs: KeyValuePairs = ["john": 1,"ben": 2,"bob": 3,"hans": 4]
print(pairs.first!)
//prints (key: "john", value: 1)
if your keys confirm to Comparable, you can create a sorted dictionary from your unsorted dictionary as follows
let sortedDictionary = unsortedDictionary.sorted() { $0.key > $1.key }
As Matt says, dictionaries (and sets) are unordered collections in Swift (and in Objective-C). This is by design.
If you want you can create an array of your dictionary's keys and sort that into any order you want, and then use it to fetch items from your dictionary.
NSDictionary has a method allKeys that gives you all the keys of your dictionary in an array. I seem to remember something similar for Swift Dictionary objects, but I'm not sure. I'm still learning the nuances of Swift.
EDIT:
For Swift Dictionaries it's someDictionary.keys
You can use the official OrderedDictionary from the original Swift Repo
The ordered collections currently contain:
Ordered Dictionary (That you are looking for)
Ordered Set
They said it is going to be merged in the Swift itself soon (in WWDC21)
Swift does not include any built-in ordered dictionary capability, and as far as I know, Swift 2 doesn't either
Then you shall create your own. You can check out these tutorials for help:
http://timekl.com/blog/2014/06/02/learning-swift-ordered-dictionaries/
http://www.raywenderlich.com/82572/swift-generics-tutorial
I know i am l8 to the party but did you look into NSMutableOrderedSet ?
https://developer.apple.com/reference/foundation/nsorderedset
You can use ordered sets as an alternative to arrays when the order of
elements is important and performance in testing whether an object is
contained in the set is a consideration—testing for membership of an
array is slower than testing for membership of a set.
var orderedDictionary = [(key:String, value:String)]()
As others have said, there's no built in support for this type of structure. It's possible they will add an implementation to the standard library at some point, but given it's relatively rare for it to be the best solution in most applications, so I wouldn't hold your breath.
One alternative is the OrderedDictionary project. Since it adheres to BidirectionalCollection you get most of the same APIs you're probably used to using with other Collection Types, and it appears to be (currently) reasonably well maintained.
Here's what I did, pretty straightforward:
let array = [
["foo": "bar"],
["foo": "bar"],
["foo": "bar"],
["foo": "bar"],
["foo": "bar"],
["foo": "bar"]
]
// usage
for item in array {
let key = item.keys.first!
let value = item.values.first!
print(key, value)
}
Keys aren't unique as this isn't a Dictionary but an Array but you can use the array keys.
use Dictionary.enumerated()
example:
let dict = [
"foo": 1,
"bar": 2,
"baz": 3,
"hoge": 4,
"qux": 5
]
for (offset: offset, element: (key: key, value: value)) in dict.enumerated() {
print("\(offset): '\(key)':\(value)")
}
// Prints "0: 'bar':2"
// Prints "1: 'hoge':4"
// Prints "2: 'qux':5"
// Prints "3: 'baz':3"
// Prints "4: 'foo':1"

Swift Sort Dictionary by property of an object value

I have a dictionary like <String,Loto> and Loto is object like below;
Loto:
{
"success": true,
"data": {
"oid": "64kbbqi8dbxygb00",
"hafta": 961,
"buyukIkramiyeKazananIl": "",
"cekilisTarihi": "11/04/2015",
"cekilisTuru": "SAYISAL_LOTO",
"rakamlar": "03#02#48#16#15#08",
"rakamlarNumaraSirasi": "02 - 03 - 08 - 15 - 16 - 48",
"devretti": false,
"devirSayisi": 0,
"bilenKisiler": [
{
"oid": "64kbbxi8dbxyg403",
"kisiBasinaDusenIkramiye": 7.35,
"kisiSayisi": 185712,
"tur": "$3_BILEN"
},
{
"oid": "64kbbxi8dbxyg402",
"kisiBasinaDusenIkramiye": 53.05,
"kisiSayisi": 9146,
"tur": "$4_BILEN"
},
{
"oid": "64kbbxi8dbxyg401",
"kisiBasinaDusenIkramiye": 4532.2,
"kisiSayisi": 142,
"tur": "$5_BILEN"
},
{
"oid": "64kbbxi8dbxyg400",
"kisiBasinaDusenIkramiye": 1528438.75,
"kisiSayisi": 1,
"tur": "$6_BILEN"
}
],
"buyukIkrKazananIlIlceler": [
{
"il": "10",
"ilView": "BALIKESÄ°R",
"ilce": "01001",
"ilceView": "AYVALIK"
}
],
"kibrisHasilati": 51127,
"devirTutari": 0.09,
"kolonSayisi": 10537872,
"kdv": 1599672.97,
"toplamHasilat": 10537872,
"hasilat": 8938199.03,
"sov": 893819.9,
"ikramiyeEH": 8044379.129999999,
"buyukIkramiye": 1528432.03,
"haftayaDevredenTutar": 0
}
}
So my dictionary like <"11042015",Loto> and i want to sort this dictionary by "hafta" property of loto object.
How can i do this? Please help me!
If Loto is an object with a hafta property, you can sort your dictionary by passing it into the sorted function, along with a closure that tells it how to order the entries:
sorted(dict) { $0.1.hafta < $1.1.hafta }
($0.1 and $1.1 because dictionaries present as a sequence of key/value pairs - you want to sort by a property of the value i.e. tuple entry 1)
Note, this will give you back a sorted array, of type [(String,Loto)] pairs, rather than a dictionary (as Swift dictionaries are unordered).
(if Loto is not really an object but rather another dictionary, you might need to do {$0.1["hafta"] < $1.1["hafta"]} - it really depends on how you’re holding your data - the good news is you don’t need to worry about optionals, since they can be compared with <)
If you don’t need the keys, just sort the values:
sorted(dict.values) { $0.hafta < $1.hafta }
which will give you back a sorted array of type [Loto]
Dictionaries are not ordered. What you need is to convert it to array, and then sort it. So something like this.
let lotoArray = lotoDictionary.allObjects as [Loto]
lotoArray.sort { $0.hafta < $1.hafta }
You can't. The keys of a dictionary are not sorted. You can get an array of the values and sort that array.

Resources