swift NSDictionary filter using string - ios

What I need is how we need to filter NSDictionary and return only the values and the keys where the key contain a string
For example if we have NSDictionary that contain :
{
"houssam" : 3,
"houss" : 2,
"other" : 5
}
and the string is "houss"
so we need to return
{
"houssam" : 3,
"houss" : 2
}
Best Regards,

You can use the filter function to get what you need like in the following way:
var dict: NSDictionary = ["houssam": 3, "houss": 2, "other": 5 ]
let string = "houss"
var result = dict.filter { $0.0.containsString(string)}
print(result) //[("houssam", 3), ("houss", 2)]
The above code return a list of tuples, if you want to get a [String: Int] dictionary again you can use the following code:
var newData = [String: Int]()
for x in result {
newData[x.0 as! String] = x.1 as? Int
}
print(newData) //["houssam": 3, "houss": 2]
I hope this help you.

Use this code to get matching keys.
var predicate = NSPredicate(format: "SELF like %#", "houss");
let matchingKeys = dictionary.keys.filter { predicate.evaluateWithObject($0) };
Then just fetch entries which keys are in matchingKeys array.

Since this is an NSDictionary, you can use the filteredArrayUsingPredicate: method on the array of keys, and fetch back the values from the initial dictionary.
For instance:
let data: NSDictionary = // Your NSDictionary
let keys: NSArray = data.allKeys
let filteredKeys: [String] = keys.filteredArrayUsingPredicate(NSPredicate(format: "SELF CONTAINS[cd] %#", "houss")) as! [String]
let filteredDictionary = data.dictionaryWithValuesForKeys(filteredKeys)
Hope that helps.

Related

Flatten an array of dictionaries to one dictionary

I am having an array of dictionaries with columnId and columnValue as a pair. Now i need to flatten it as columnId as the key and columnValue as the value of it. How is it possible to do with swift higher order functions?
let arrayOfDictionaries = [["columnId": 123, "columnValue": "sample text"], ["columnId": 124, "columnValue": 9977332]]
//The end result should be:
flattenedDictionary: [String: Any] = ["123": "sample text", "124": 9977332]
Note: Result dictionary will be in the form of [String: Any]
This would work:
func flatten(_ pairs: [[String: Any]]) -> [String: Any] {
pairs.reduce(into: [String: Any]()) {
if let id = $1["columnId"] as? Int, let value = $1["columnValue"] {
$0["\(id)"] = value
}
}
}
You can do this in two steps;
Convert your input array into a sequence of key-value pairs using compactMap
Convert the sequence back into a dictionary using Dictionary(uniqueKeysWithValues:)
let arrayOfDictionaries = [["columnId": 123, "columnValue": "sample text"], ["columnId": 124, "columnValue": 9977332]]
let tupleArray:[(String,Any)] = arrayOfDictionaries.compactMap { dict in
guard let id = dict["columnId"], let value = dict["columnValue"] else {
return nil
}
return ("\(id)",value)
}
let flattenedDictionary: [String: Any] = Dictionary(uniqueKeysWithValues: tupleArray)
Note that this code will throw an exception if there are duplicate keys. You should either take steps to ensure the columnId values are unique or use Dictionary(keysAndValues:, uniquingKeysWith:) to resolve id clashes.

How to append JSON values into array using Swift 4?

I am trying to get JSON values and appending into array. Here, below code add_user_product have a chance to come null. If it is null need to append null into array and if not null need to store ID also.
I am trying to get output like - [10,null,12,13,null,……]
// add_user_products & If add_user_product == null need to store null otherwise add_user_product ["id"]
if let add_user_product = fields[“add_user_product"] as? [String : Any] {
let add_id = add_user_product["id"] as! Int
self.addlistIDData.append(add_id)
}
else {
//print("Failure")
}
below my sample response
{
"students":[
{
"grade":0,
"add_user_product":
{
"id":10
}
},
{
"grade":1,
"add_user_product":null
},
{
"grade":2,
"add_user_product":
{
"id":11
}
}
]
}
Expected output: [10,null,11,......] //This array I am going to use Tableview cell
I suggest use nil instead of null string.
Declare your addlistIDData type as [Int?] where Int is an Optional.
Consider below example I have created for you:
var addlistIDData: [Int?] = [10, nil, 12, 13, nil] //Created array just for example
//created dict for testing purpose
let fields: [String : Any]? = ["add_user_product": ["id": nil]]
if let field = fields {
if let add_user_product = field["add_user_product"] as? [String:Any] {
let add_id = add_user_product["id"] as? Int
//now append your object here weather it's a nil or it has any value
addlistIDData.append(add_id)
}
}
else {
//print("Failure")
}
print(addlistIDData)
And output will be:
[Optional(10), nil, Optional(12), Optional(13), nil, nil]
PS: You need to cast an object with if let or with guard let whenever you are accessing objects from this addlistIDData array.
null will not be identifiable, the only way to store it in your array would be to store it as String, But for that also you'll have to store othere elements as String.
But i would suggest instead of adding null just add 0 as:
var arr = [Int]()
if let add_user_product = fields["add_user_product"] as? [String: Any] {
if let productId = add_user_product["id"] as? Int {
arr.append(productId)
} else {
arr.append(0)
}
} else {
//
}
You can do like this:
var resultArray = [Int?]()
if let add_user_product = fields["add_user_product"] as? [String: Any] {
if let add_id = add_user_product["id"] as? Int {
resultArray.append(add_id)
} else {
resultArray.append(nil)
}
} else {
//print("Failure")
}
Hope this Helps.
You can use compactMap:
let arrayDict = [ ["id" : 3], ["id" : nil], ["id" : 5] ]
let result = arrayDict.compactMap { $0["id"] }
print(result)
Output:
[Optional(3), nil, Optional(5)]

How to use filteredArrayUsingPredicate with NSDictionary?

Now I'm using
let array = (displayNames as NSArray).filteredArrayUsingPredicate(searchPredicate)
where displayNames is
var displayNames[String]()
But I want to use it with:
var displayNames[String: UIImage]()
How can I use .filteredArrayUsingPredicate with displayNames string part in NSDictionary?
Get the keys from the dictionary using the allKeys property and perform the filter on that. Try that.
RE-EDIT: Try something like this
let theOriginalDictionary = [String : UIImage]()
let searchPredicate = NSPredicate(format: "", argumentArray: nil)
let otherDictionary = NSDictionary(dictionary: theOriginalDictionary)
let arrayOfKeys = NSArray(array: otherDictionary.allKeys)
let filteredArray = arrayOfKeys.filteredArrayUsingPredicate(searchPredicate)
If you're using Swift, why don't you use built-in functions like filter?
var displayNames = [String: UIImage]()
let result = displayNames.filter {key,image in
return true // here add your predicate to filter, true means returning all
}
It sounds like you're trying to filter the dictionary, while keeping the dictionary type after the filter. There's a few ways to do this, but maybe the easiest is to extend dictionary:
extension Dictionary {
func filter(#noescape includeElement: (Key, Value) throws -> Bool) rethrows -> [Key:Value] {
var result: [Key:Value] = [:]
for (k,v) in self where try includeElement(k,v) {
result[k] = v
}
return result
}
}
Then, if you wanted to filter a dictionary based on the keys, you can do something like this:
let dict = ["a": 1, "b": 2, "c": 3]
let filtered = dict.filter { (k,_) in k != "a" }
// ["b": 2, "c": 3]

How to assign an array element which can be null in Swift?

In my Swift app, I'm querying an api returning a json object like this :
{
key1: value1,
key2: value2,
array : [
{
id: 1,
string: "foobar"
},
{
id: 2,
string: "foobar"
}
]
}
Facts :
The array value can be empty.
I want to read the first array element,
present or not.
In Swift i'm doing :
if let myArray: NSArray = data["array"] as? NSArray {
if let element: NSDictionary = myArray[0] as? NSDictionary {
if let string: NSString = element["string"] as? NSString {
// i should finally be able to do smth here,
// after all this crazy ifs wrapping
}
}
}
It works if the array and the first element exist, but i'm having a crash with "index 0 beyond bounds for empty array" even if the element assignment is within an if let wrapping.
What i am doing wrong here ? I'm getting crazy with Swift optionals, typing and crazy if let wrapping everywhere...
The error is not concerning about Optional. If you use subscription([]) for array, you have to check the length of that.
if let myArray: NSArray = data["array"] as? NSArray {
if myArray.count > 0 { // <- HERE
if let element: NSDictionary = myArray[0] as? NSDictionary {
if let string: NSString = element["string"] as? NSString {
println(string)
}
}
}
}
But we have handy .firstObject property
The first object in the array. (read-only)
If the array is empty, returns nil.
Using this:
if let myArray: NSArray = data["array"] as? NSArray {
if let element: NSDictionary = myArray.firstObject as? NSDictionary {
if let string: NSString = element["string"] as? NSString {
println(string)
}
}
}
And, we can use "Optional Chaining" syntax:
if let str = (data["array"]?.firstObject)?["string"] as? NSString {
println(str)
}
You can use this
var a = [Any?]()
a.append(nil)
If you have a non-optional array with AnyObject you can use NSNull (like in Obj-C)

Array from dictionary keys in swift

Trying to fill an array with strings from the keys in a dictionary in swift.
var componentArray: [String]
let dict = NSDictionary(contentsOfFile: NSBundle.mainBundle().pathForResource("Components", ofType: "plist")!)
componentArray = dict.allKeys
This returns an error of: 'AnyObject' not identical to string
Also tried
componentArray = dict.allKeys as String
but get: 'String' is not convertible to [String]
Swift 3 & Swift 4
componentArray = Array(dict.keys) // for Dictionary
componentArray = dict.allKeys // for NSDictionary
With Swift 3, Dictionary has a keys property. keys has the following declaration:
var keys: LazyMapCollection<Dictionary<Key, Value>, Key> { get }
A collection containing just the keys of the dictionary.
Note that LazyMapCollection that can easily be mapped to an Array with Array's init(_:) initializer.
From NSDictionary to [String]
The following iOS AppDelegate class snippet shows how to get an array of strings ([String]) using keys property from a NSDictionary:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let string = Bundle.main.path(forResource: "Components", ofType: "plist")!
if let dict = NSDictionary(contentsOfFile: string) as? [String : Int] {
let lazyMapCollection = dict.keys
let componentArray = Array(lazyMapCollection)
print(componentArray)
// prints: ["Car", "Boat"]
}
return true
}
From [String: Int] to [String]
In a more general way, the following Playground code shows how to get an array of strings ([String]) using keys property from a dictionary with string keys and integer values ([String: Int]):
let dictionary = ["Gabrielle": 49, "Bree": 32, "Susan": 12, "Lynette": 7]
let lazyMapCollection = dictionary.keys
let stringArray = Array(lazyMapCollection)
print(stringArray)
// prints: ["Bree", "Susan", "Lynette", "Gabrielle"]
From [Int: String] to [String]
The following Playground code shows how to get an array of strings ([String]) using keys property from a dictionary with integer keys and string values ([Int: String]):
let dictionary = [49: "Gabrielle", 32: "Bree", 12: "Susan", 7: "Lynette"]
let lazyMapCollection = dictionary.keys
let stringArray = Array(lazyMapCollection.map { String($0) })
// let stringArray = Array(lazyMapCollection).map { String($0) } // also works
print(stringArray)
// prints: ["32", "12", "7", "49"]
Array from dictionary keys in Swift
componentArray = [String] (dict.keys)
You can use dictionary.map like this:
let myKeys: [String] = myDictionary.map{String($0.key) }
The explanation:
Map iterates through the myDictionary and accepts each key and value pair as $0. From here you can get $0.key or $0.value. Inside the trailing closure {}, you can transform each element and return that element. Since you want $0 and you want it as a string then you convert using String($0.key). You collect the transformed elements to an array of strings.
dict.allKeys is not a String. It is a [String], exactly as the error message tells you (assuming, of course, that the keys are all strings; this is exactly what you are asserting when you say that).
So, either start by typing componentArray as [AnyObject], because that is how it is typed in the Cocoa API, or else, if you cast dict.allKeys, cast it to [String], because that is how you have typed componentArray.
extension Array {
public func toDictionary<Key: Hashable>(with selectKey: (Element) -> Key) -> [Key:Element] {
var dict = [Key:Element]()
for element in self {
dict[selectKey(element)] = element
}
return dict
}
}
dict.keys.sorted()
that gives [String]
https://developer.apple.com/documentation/swift/array/2945003-sorted
From the official Array Apple documentation:
init(_:) - Creates an array containing the elements of a sequence.
Declaration
Array.init<S>(_ s: S) where Element == S.Element, S : Sequence
Parameters
s - The sequence of elements to turn into an array.
Discussion
You can use this initializer to create an array from any other type that conforms to the Sequence protocol...You can also use this initializer to convert a complex sequence or collection type back to an array. For example, the keys property of a dictionary isn’t an array with its own storage, it’s a collection that maps its elements from the dictionary only when they’re accessed, saving the time and space needed to allocate an array. If you need to pass those keys to a method that takes an array, however, use this initializer to convert that list from its type of LazyMapCollection<Dictionary<String, Int>, Int> to a simple [String].
func cacheImagesWithNames(names: [String]) {
// custom image loading and caching
}
let namedHues: [String: Int] = ["Vermillion": 18, "Magenta": 302,
"Gold": 50, "Cerise": 320]
let colorNames = Array(namedHues.keys)
cacheImagesWithNames(colorNames)
print(colorNames)
// Prints "["Gold", "Cerise", "Magenta", "Vermillion"]"
Swift 5
var dict = ["key1":"Value1", "key2":"Value2"]
let k = dict.keys
var a: [String]()
a.append(contentsOf: k)
This works for me.
NSDictionary is Class(pass by reference)
Dictionary is Structure(pass by value)
====== Array from NSDictionary ======
NSDictionary has allKeys and allValues get properties with
type [Any].
let objesctNSDictionary =
NSDictionary.init(dictionary: ["BR": "Brazil", "GH": "Ghana", "JP": "Japan"])
let objectArrayOfAllKeys:Array = objesctNSDictionary.allKeys
let objectArrayOfAllValues:Array = objesctNSDictionary.allValues
print(objectArrayOfAllKeys)
print(objectArrayOfAllValues)
====== Array From Dictionary ======
Apple reference for Dictionary's keys and values properties.
let objectDictionary:Dictionary =
["BR": "Brazil", "GH": "Ghana", "JP": "Japan"]
let objectArrayOfAllKeys:Array = Array(objectDictionary.keys)
let objectArrayOfAllValues:Array = Array(objectDictionary.values)
print(objectArrayOfAllKeys)
print(objectArrayOfAllValues)
This answer will be for swift dictionary w/ String keys. Like this one below.
let dict: [String: Int] = ["hey": 1, "yo": 2, "sup": 3, "hello": 4, "whassup": 5]
Here's the extension I'll use.
extension Dictionary {
func allKeys() -> [String] {
guard self.keys.first is String else {
debugPrint("This function will not return other hashable types. (Only strings)")
return []
}
return self.flatMap { (anEntry) -> String? in
guard let temp = anEntry.key as? String else { return nil }
return temp }
}
}
And I'll get all the keys later using this.
let componentsArray = dict.allKeys()
// Old version (for history)
let keys = dictionary.keys.map { $0 }
let keys = dictionary?.keys.map { $0 } ?? [T]()
// New more explained version for our ducks
extension Dictionary {
var allKeys: [Dictionary.Key] {
return self.keys.map { $0 }
}
}

Resources