I'm handling a dictionary which I need to parse into a string to create a property for a GraphQL mutation.
My dictionary is [String: Bool] and I need to extract the keys set to true.
let dict: [String: Bool] = ["Objct1": true, "Objct2": false, "Objct3": true]
Currently I'm using four functions (filter, enumerate, map and join) and, although I get the desired result, I wonder if I need all of them:
let string = dict.filter { $0.value }
.enumerated()
.map {
return $0.offset == 0
? "\"\($0.element.key)\""
: ", \"\($0.element.key)\""
}
.joined()
Edit:
The final string needs to be wrapped in ": ""Objct1", "Objct3""
You can use a single compactMap, since first you'd need to filter your Dictionary to only keep the true values, then you'd need to map to return the key corresponding to the true values. However, you can always merge consequent filter and map calls into a single compactMap, since compactMap only keeps non-nil values, so instead of returning a boolean as you would for filter, in case your condition evaluates to true, you return whatever you would return in map, otherwise you return nil.
let trueKeys = dict.compactMap({key, value in value ? key : nil})
To join the keys into a single String, you can just call joined on the result of compactMap.
let graphQueryString = dict.compactMap({key, value in value ? "\"\(key)\"" : nil}).joined(separator: ",") // ""Objct3","Objct1""
Keep in mind that the ordering of your keys won't necessarily be the same as you declared the Dictionary, since Dictionary is an unordered collection by definition. If you want to keep ordering, you can use an array of tuples instead of a Dictionary, where the tuple will consist of the key-value pairs.
Related
I am wondering why map format has to be {( )} rather than just { }
func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
// the following is right
var num1Reduce = nums1.reduce(0){ $0 + $ 1}
/// the following is wrong ??
var num2Dict = Dictionary(nums2.map{ $0, 1 }, uniquingKeysWith : +)
// the following is right
var num1Dict = Dictionary(nums1.map{ ($0, 1) }, uniquingKeysWith : +)
}
and I even see the following format ({ }). I am totally confused!
let cars = peopleArray.map({ $0.cars })
print(cars)
You are using the following Dictionary initializer:
init<S>(_ keysAndValues: S, uniquingKeysWith combine: (Dictionary<Key, Value>.Value, Dictionary<Key, Value>.Value) throws -> Dictionary<Key, Value>.Value) rethrows where S : Sequence, S.Element == (Key, Value)
Note that S is a sequence where its elements are a tuple of key/value pairs.
When you pass nums1.map{ ($0, 1) } to the first parameter, you are creating an array of key/value tuples from nums1.
It fails when you use nums2.map{ $0, 1 } because that is missing the parentheses for the tuple.
Keep in mind that nums1.map{ ($0, 1) } is shorthand for nums1.map({ ($0, 1) }). That's all related to trailing closures which has nothing to do with the parentheses for the tuple that appear inside the { }.
A map is a function that takes a closure as a parameter. We can call the map and pass the parameter like we do for any other ordinary function call without removing the brackets ()e.g
(0...100).map ({ _ in print("yeti")})
But swift allows us to remove the brackets as a way of shorthanding and we can write it like, hence eliminating the ()
(0...100).map { _ in print("yeti")}
But incase you want to access individual values of the array elements, you can do so in two ways,
Given an array, you can access it's individual element using $0, which basically says, Hey map, give me the first element at this current index.
(0...100).map {$0}
Instead of using the default swift indexing, you decide to define the value you are accessing by giving it a readable variable name e.g
(0...100).map {element in}
This gets $0 and assigns it to element, the in keyword basically tells the compiler that hey, $0 is now element and we are going to use it after in. Otherwise if you remove the in keyword, the compiler says it doesn't know any variable called element.
For special collections like dictionaries, they have two values per index, i.e the key and value, therefore if you want to access the contents of a dictionary during the mapping, you can do it in two ways like above, a). use the default swift indexes, or give the values per index, readable variable names. e.g
let dictionary = ["a": 3, "b": 4, "c": 5]
dictionary.map{($0, $1)}
We use inner brackets () to let the compiler know that the collection we are mapping over has two values per index. Please note the inner parenthesis are creating a tuple
dictionary.map {(key, value) in }
Frankly speaking, When I try to do filtering for the dictionary with the following key and value pair ["deviceId":21,"geofenceId":34], it's order get changed randomly. But, As a matter of fact, I want to be in the same order the whole time. How to do that with the same [String:Any] type.
Dictionary collection is unordered, but you can sort the keys though
let myDict = ["geofenceId":34, "deviceId": 1]
let sortedKeys = myDict.keys.sorted(by: { $0 < $1 })
print(sortedKeys)
Now you can loop through the sorted keys and access the item from dictionary.
I have an array of dictionaries called groupedDictionary defined below:
// The type is [String : [SingleRepository]]
let groupedDictionary = Dictionary(grouping: finalArrayUnwrapped) { (object) -> String in
var language = "Not Known to GitHub"
if let languageUnwrapped = object.language {
language = languageUnwrapped
}
return language
}
I can easily get all the keys as follows:
let keys = groupedDictionary.keys
However, when I try to sort this array using sorted(by:) in Swift 4, I successfully get the sorted array back with the type [(key: String, value: [SingleRepository])].
// the type is [(key: String, value: [SingleRepository])]
let sortedGroupedDictionary = groupedDictionary.sorted(by: { ($0.value.count) > ($1.value.count) })
How can I get all of the keys from sortedGroupedDictionary?
It is not possible to call ".keys" on sortedGroupedDictionary, since it has a different type.
Please note: I'm not trying to sort the array based on the keys. I did sort the array that consists of dictionaries, based on a predicate which is size of the array containing each value, now I just want to extract the keys.
The method Dictionary.sorted(by:) returns the keys and values of your original dictionary as an array of key-value pairs, sorted by the predicate you pass as an argument. That means that the first element of each tuple is the key you're looking for.
You can go through the result like this:
for (key, value) in sortedGroupedDictionary {
// handle this key-value-pair
}
If all you need is an array of the sorted keys, you can get that using
sortedGroupedDictionary.map { $0.key }
I have this Dictionary, which I am getting from a web service:
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! Dictionary<String, AnyObject>
and now I am trying to sort them alphabetically like so:
self.appDelegate.communityArray = json.sorted(by: {$0.0 < $1.0})
But I get this error:
Cannot assign value of type '[(key: String, value: AnyObject)]' to
type 'Dictionary?'
What am I doing wrong?
This is how I am defining communityArray:
var communityArray: Dictionary<String, AnyObject>?
As mentioned in the comments a dictionary – a collection type containing key-value pairs – is unordered by definition, it cannot be sorted.
The sorted function of collection applied to a dictionary treats the dictionary for example
["foo" : 1, "bar" : false]
as an array of tuples
[(key : "foo", value : 1), (key : "bar", value : false)]
and sorts them by key (.0) or value (.1) (I suspect sorting by value Any will raise a compiler error)
The error occurs because you cannot assign an array of tuples to a variable declared as dictionary. That's almost the literal error message.
As a compromise I recommend to map the dictionary to a custom struct (similar to a tuple but better to handle)
struct Community {
let key : String
let value : Any
}
Your variable name already implies that you want a real array
var communityArray = [Community]()
let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary<String, Any>
communityArray = json.map { Community(key: $0.0, value: $0.1 }.sorted { $0.key < $1.key }
sorted(by:) Returns the elements of the sequence, sorted using the given predicate as the comparison between elements, so you can not use as a dictionary.
For more info about this function you may read sorted(by:) documentation
I am trying to remove null value for key in dictionary
so I have this kind of data:
let dic = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
let array = [dic,2,3,4]
let jsonResult:[String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": array,"About": NSNull()]
let jsonCleanDictionary = filter(jsonResult, {!($0.1 is NSNull)})
can not understand syntax of above filter function
Do not use NSNull() in swift instead prefer using nil. Further, since its a dictionary adding keys with a null value is pretty useless since dictionaries will return nil if the key doesn't exist. So when checking for null all you have to do is
if let some = dic["key"] as? Value {
// some now contains the value inside dic's key as a value type of Value.
}
Also the filter function works by taking a block which returns a bool so:
dict.filter { (key, value) -> Bool in
// Do stuff to check key and value and return a
// bool which is true if you want that key, value pair to
// appear in the filtered result.
}
In swift closure arguments can get anonymous names if not explicitly return. These names are of the format $0, $1, etc. Now, the filter function takes only parameter specifically the Self.Generator.Element from the CollectionType protocol. For dictionaries this is a tuple containing the key and the value. To access members of unnamed tuples you use .0, .1, .2, etc. depending on the index of the tuple member. So for dictionaries Self.Generator.Element is a tuple containing the key and the value. So $0.1 refers to the value of the key,value pair. Hope this clears this weird syntax up a little bit.