Array to nested Dictionary in iOS? - ios

There are a lot of questions with similar title but no one answers my question.
In my case I have an array:
["1", "2", "3", "4"]
And resulting dictionary should be:
["1": ["2": ["3": "4"]]]
How to convert it correctly? As I understand map, reduce and the similar functions doesn't have such functionality. And is it possible to do without of writing a recursive method?
Both Objective-C and Swift are applicable

A Swift solution, using reduce:
let a = ["1", "2", "3", "4"]
let d = a.dropLast(2).reversed()
.reduce([a[a.count - 2] : a[a.count - 1]]) { [$1 : $0] }
print(d) // ["1": ["2": ["3": "4"]]]
Here it is assumed that the array has at least 2 elements.
In our example, ["3", "4"] is the initial value for reduce,
and for the remaining elements in reverse order, a nested dictionary
is created in the closure with [$1 : $0].
The type of the result is [String: Any], which bridges to
NSDictionary if necessary.

//Your array
NSArray *a = #[#1,#2,#3,#4];
//Get the two last values (considering your array length must be at least 2 !
NSDictionary *last = #{a[a.count-2]:a[a.count-1]};
//In reverse order we assign last created dictionary to the current key, the array length must be superior than 3
for (NSInteger i = a.count - 3; i >= 0; i--)
last = #{a[i]:last};
NSLog(#"My dictionary %#", last);

Recursive option.
func toNestedDictionary(arr: [String]) -> [String: Any?]? {
if arr.isEmpty {
return nil
}
else if arr.count == 1 {
return Dictionary(dictionaryLiteral: (arr.first!, nil))
}
else if arr.count == 2 {
return Dictionary(dictionaryLiteral: (arr.first!, arr.last!))
}
else {
let head = arr.first!
let tail = arr.dropFirst()
return Dictionary(dictionaryLiteral: (head, toNestedDictionary(arr: Array(tail))))
}
}
print (toNestedDictionary(arr: ["1"]))
// Optional(["1": nil])
print (toNestedDictionary(arr: ["1", "2"]))
// Optional(["1": Optional("2")])
print (toNestedDictionary(arr: ["1", "2", "3"]))
// Optional(["1": Optional(["2": Optional("3")])])
print (toNestedDictionary(arr: ["1", "2", "3", "4"]))
// Optional(["1": Optional(["2": Optional(["3": Optional("4")])])])

Try below lines without recursion
NSArray *arr = #[#"1", #"2", #"3", #"4"];
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
for (int i = arr.count-2; i>=0;i--) {
if (i == arr.count-2) {
[dic setObject:arr[i+1] forKey:arr[i]];
} else {
NSDictionary *tempDic = dic;
dic = #{arr[i]:tempDic};
}
}
NSLog(#"Solution%#",dic);

This problem can be solved using recursive programming easily. Here is the solution using Swift;
var ary = ["1", "2", "3", "4"]
func recursive(array:[String]) -> Any {
if ary.count > 1 {
let key = ary.removeFirst()
return [key : recursive(array: array)]
} else {
return array.last!
}
}
let reuslt = recursive(array: ary)
And result is: ["1": ["2": ["3": "4"]]]

Given a list
var list = ["1", "2", "3", "4"]
you can write
if let lastValue = list.popLast(), let lastKey = list.popLast() {
let dict = list.reversed().reduce([lastKey:lastValue]) { [$1:$0] }
print(dict)
}
Result
["1": ["2": ["3": "4"]]]
Please note that this code only works if the list contains at least 2 elements

Found a simplier solution:
NSMutableArray *tempArr = [array mutableCopy];
while (tempArray.count > 1) {
id value = tempArr.lastObject;
[tempArr removeLastObject];
NSString *key = tempArr.lastObject;
[tempArr removeLastObject];
[tempArr addObject:#{key: value}];
}
id result = tempArr.firstObject;

Related

How to sort one array based on the order of its object in another array in objective-c?

I have list which is sorted by user. then I get the list from server again.
some elements might be deleted or added.
I want to sort the new array based on the sorting of the previous array then add new elements.
Example
oldElementList: [1, 4, 2, 8] ---> user set this order
newElementList: [1, 4, 3, 8]
What I want as output is: [1, 4, 8, 3]
Actually element are not numbers they are objects. and when they are gotten from server some of their property values might have changed.
My answer:
for (ElementModel *oldElement in oldElementList) {
for (ElementModel * newElement in newElementList) {
if ([newElement.number isEqualToString: oldElement.number]) {
[sortedArray addObject: newElement];
[newElementList removeObject: newElement];
break;
}
}
}
for (ElementModel *newElement in newElementList) {
[sortedArray addObject: newElement];
}
I don't think my answer is ok, I want to make it better in performance or in any other aspect that I have not considered.
Which sorting algorithm is appropriate depends very much on your data, e.g.:
- Is the data set to be sorted large (only then, a sophisticated algorithms may pay off)?
- Is the data set to be sorted completely random or presorted?
From your description it seems to me that you have a large data set (otherwise any algorithms giving the correct result would be probably fine, including your own that has a O(n^2) complexity), and that it is presorted, i.e. there are only only a few additions and deletions (otherwise it may not be so important to keep the original sorting).
If so, what about the following algorithm (sorry, it is in Swift, but could surely easily be converted to Obj-C):
let old = [1, 4, 2, 7, 8]
let new = [1, 4, 3, 8, 2]
var oldIndexed: [Int: Int] = [:]
for i in 0 ..< old.count {
oldIndexed[old[i]] = i
}
var newIndexed: [Int: Int] = [:]
for i in 0 ..< new.count {
newIndexed[new[i]] = oldIndexed[new[i]] ?? old.count
}
var resultArray: [(Int, Int)] = []
for (key, value) in newIndexed {
resultArray.append((key, value))
}
resultArray = resultArray.sorted { (first, second) -> Bool in
first.1 < second.1
}
let result = resultArray.map{ $0.0 } // Here: [1, 4, 2, 8, 3]
The idea is to give the old data elements an index, and every new data element the same index. This is done by using dictionaries, since there every element can be accessed by its key in O(1). New elements get a larger index (this could also be a counter to make it clearer). Then the new dictionary is converted back to an array, that is then sorted by its index. Eventually, the index is dropped, and the result is ready.
I guess, the complexity of this algorithm is determined by the sort function that should be optimal since it is implemented in the standard library.
EDIT:
I did not program in Obj-C for a long time, but tried it just for fun once more:
NSArray *old = #[#1, #4, #2, #7, #8];
NSArray *new = #[#1, #4, #3, #8, #2];
NSMutableDictionary *oldIndexed = [[NSMutableDictionary alloc] init];
for (int i = 0; i < old.count; i++) {
[oldIndexed setValue:[NSNumber numberWithInt: i] forKey: old[i]];
}
NSMutableDictionary *newIndexed = [[NSMutableDictionary alloc] init];
long counter = old.count;
for (int i = 0; i < old.count; i++) {
NSNumber *oldIndexOfNewValue = oldIndexed[new[i]];
NSNumber *newIndex;
if (oldIndexOfNewValue != nil) {
newIndex = oldIndexOfNewValue;
} else {
newIndex = [NSNumber numberWithLong: counter];
counter++;
}
[newIndexed setValue: newIndex forKey: new[i]];
}
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
NSArray *allKeysInNewIndexed = newIndexed.allKeys;
for (int i = 0; i < allKeysInNewIndexed.count; i++) {
NSNumber *nextKey = allKeysInNewIndexed[i];
NSArray *nextPair = #[nextKey, newIndexed[nextKey]];
[resultArray addObject: nextPair];
}
NSArray *sortedResultArray;
sortedResultArray = [resultArray sortedArrayUsingComparator: ^NSComparisonResult(NSArray *first, NSArray *second) {
NSNumber *firstIndex = first[1];
NSNumber *secondIndex = second[1];
return [firstIndex compare: secondIndex];
}];
NSMutableArray * result = [[NSMutableArray alloc] init];
for (int i = 0; i < sortedResultArray.count; i++) {
[result addObject: sortedResultArray[i][0]];
}

Replace an Dictionary object in Array using NSPredicate

I have one dictionary object in Array.
I want to replace this object with new dictionary.
Both have same order_id.
Currently I am doing it like, How can I do it with NSPredicate.
NSMutableArray *orderList=[[NSUserDefaults standardUserDefaults] objectForKey:#"Orders"];
//2. Find and replace the object/OrdeDetails.
for(int i=0;i<orderList.count;i++){
NSDictionary *dictionary=orderList[i];
if([dictionary[#"order_id"] isEqualToString:OrderDetails[#"order_id"]]){
[orderList replaceObjectAtIndex:i withObject:OrderDetails];
break;
}
}
Check this code:
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.order_id contains[cd] %#",OrderDetails[#"order_id"]];
NSMutableArray *arrOrders = [[NSMutableArray alloc]init];
[arrOrders addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"Orders"]];
NSArray *filteredOrder = [arrOrders filteredArrayUsingPredicate:resultPredicate];
if ([filteredOrder count] > 0) {
NSUInteger index = [arrOrders indexOfObject:[filteredOrder objectAtIndex:0]];
if (index != NSNotFound) {
[arrOrders replaceObjectAtIndex:index withObject:OrderDetails];
}
}
You cannot replace an object with NSPredicate you can however search for it then do the replacement afterwards.
Well just made this up without testing but I do think it has a valid syntax.
You can use this as the foundation or hope you get the logic in using the predicate.
// construct the predicate with the given condition
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"order_id = %#",dictionary[#"order_id"]];
// this will filter the array according to the given predicate. if there are more than 1 entries with the given condition then you should handle that this only handle unique entries
NSArray *array = [orderList filteredArrayUsingPredicate:predicate];
// assuming that order_id is unique
NSInteger index = [orderList indexOfObject:[array lastObject]];
if (index != NSNotFound) // check if index is existing
[orderList replaceObjectAtIndex:index withObject:orderDetails]; // replace the object with the desired object
If I want to match perfect then I used, LIKE. Although I am not sure.
Got from the linkcheat sheet
NSPredicate *predicateSearchOrder=[NSPredicate predicateWithFormat:#"SELF.order_id LIKE[cd] %#",[responsedDict valueForKey:#"order_id"]];
//
// //It will be only one object with order id.
NSArray *searchedArray=[orderList filteredArrayUsingPredicate:predicateSearchOrder];
if(searchedArray.count>0){
NSDictionary *toModiFyDictionary=searchedArray[0];
toModiFyDictionary=OrderDetails;
}
This is also working
If you were using Swift, It's very simple.
public class Test : NSObject {
class func test() -> Void {
var array : [Dictionary<String, String>] = []
let dic: Dictionary<String, String> = ["order_id" : "111", "name" : "good1"]
let dic2: Dictionary<String, String> = ["order_id" : "222", "name" : "good2"]
let dic3: Dictionary<String, String> = ["order_id" : "111", "name" : "good3"]
array.append(dic)
array.append(dic2)
let result = array.map { (elem) -> [String : String] in
if elem["order_id"] == "111" {
return dic3
}
return elem
}
print("result = \(result)")
}
}

It is possible to use array reduce concept from swift in objective c?

I have this lines of code in Swift:
let graphPoints:[Int] = [4, 2, 6, 4, 5, 8, 3]
let average = graphPoints.reduce(0, combine: +) / graphPoints.count
It is possible to "translate" this lines of code in objective c code?
It's not very clear for me how reduce combine concept works. I read about it but still is unclear.
I took the code from this tutorial: http://www.raywenderlich.com/90693/modern-core-graphics-with-swift-part-2
Please help. Thanks.
let's say you have some NSNumbers stored in an NSArray you can use this KVC collection operator:
NSArray *someNumbers = #[#0, #1.1, #2, #3.4, #5, #6.7];
NSNumber *average = [someNumbers valueForKeyPath:#"#avg.self"];
For Objective-C, I would add the Higher-Order-Functions to this list of answers: https://github.com/fanpyi/Higher-Order-Functions
#import <Foundation/Foundation.h>
typedef id (^ReduceBlock)(id accumulator,id item);
#interface NSArray (HigherOrderFunctions)
-(id)reduce:(id)initial combine:(ReduceBlock)combine;
#end
#import "NSArray+HigherOrderFunctions.h"
#implementation NSArray (HigherOrderFunctions)
-(id)reduce:(id)initial combine:(ReduceBlock)combine{
id accumulator = initial;
for (id item in self) {
accumulator = combine(accumulator, item);
}
return accumulator;
}
#end
example:
NSArray *numbers = #[#5,#7,#3,#8];
NSNumber *sum = [numbers reduce:#0 combine:^id(id accumulator, id item) {
return #([item intValue] + [accumulator intValue]);
}];
NSNumber *multiplier = [numbers reduce:#1 combine:^id(id accumulator, id item) {
return #([item intValue] * [accumulator intValue]);
}];
NSLog(#"sum=%#,multiplier=%#",sum,multiplier);
The reduce function is not standard in Objective-C. You can implement it as an extension of NSArray though.
In your case, you have an array of Int in Swift. You cannot have that in Objective-C, you need an array of NSNumber.
Here is an implementation of reduce that should work in your case:
#implementation NSArray (Helpers)
- (NSInteger)reduceInt:(NSInteger)initial combine:(NSInteger (^)(NSInteger acum, NSInteger element))block {
if (!self) {
return initial;
}
NSInteger acum = initial;
for (id element in self) {
if ([element isKindOfClass:[NSNumber class]]) {
acum = block(acum, [(NSNumber *)element integerValue]);
}
}
return acum;
}
#end
You can use it then with your array, something like this:
NSArray *a = #[#1, #2, #3];
NSInteger result = [a reduceInt:0 combine:^NSInteger(NSInteger acum, NSInteger element) {
return acum + element;
}];
how to translate reduce to ObjC (or better to say how to solve your "average problem" in Objective C) was perfectly answered by André Slotta. the swift reduce is much more than that. I will try to answer the second part of your question, how the concept works in swift
func reduce<T>(initial: T, #noescape combine: (T, Self.Generator.Element) throws -> T) rethrows -> T
Return the result
of repeatedly calling combine with an accumulated value initialized to
initial and each element of self, in turn, i.e. return
combine(combine(...combine(combine(initial, self[0]),
self[1]),...self[count-2]), self[count-1]).
let arr: Array<Int> = [1,2,3,4,5]
let sum = arr.reduce(0) { (sum, i) -> Int in
return sum + i
}
print(sum) // 15
// this is an quasi equivalent of
var sum1 = 0 // ..... reduce(0)....
arr.forEach { (elementValue) -> Void in
sum1 = sum1 + elementValue // ...{ return sum + i }
}
print(sum1) // 15 reduce function will return accumulated inital value
// reduce is part of SequenceType protocol, that is why
let arr1 = ["H","e","l","l","o"," ","w","o","r","l","d"]
let str = arr1.reduce("") { (str, s) -> String in
str + s
}
// works the same way
print(str) // "Hello world"
// let have a litle bit more complex example, to see how powerful, useful and easy to use reduce can be
let dict = arr1.reduce([:]) { (var dict, s) -> Dictionary<Int,String> in
let i = dict.count
dict.updateValue(s, forKey: i+1)
return dict
}
print(dict) // [11: "d", 10: "l", 2: "e", 4: "l", 9: "r", 5: "o", 6: " ", 7: "w", 3: "l", 1: "H", 8: "o"]
Write an NSArray extension
- (NSInteger)reduceStart:(NSInteger)start combine:(NSInteger(^)(NSInteger x, NSInteger y))combine {
for (NSNumber* n in self) {
if ([n isKindOfClass:[NSNumber class]]) {
start = combine (start, n.integerValue);
}
}
return start;
}
fix all mistakes that I made, and that's it. Just less flexible than Swift.

IOS Sort NSDictionary with given array order

I have a dictionary with objects and keys. I want to sort that dictionary using the keys
dict = [name : abc, city: xyz, date :xys, location: mnq ...]
for example i want to pass like
array = [name, location, city, date]
i want to sort the dicionary like
dict = [name : abc, location: mnq, city: xyz,date: xys ...]
It should sort dicationay,as per my passing array values
not like alphabet or allkeys or allValues order
NSArray *newkey = [[NSArray alloc] initWithObjects:#"name",#"location", #"city", #"date", nil];
int index = 0;
for (id key in dictionary) {
NSLog(#"key: %#, value: %#", [newkey objectAtIndex:index], [dictionary objectForKey:[newkey objectAtIndex:index]]);
index++;
}
u can't sort dictionary ... make 2D array with key - value pairs and sort them afterwards with .sort() function
var dict : NSMutableDictionary = NSMutableDictionary()
dict.setObject("damu", forKey: "name")
dict.setObject("bng", forKey: "city")
dict.setObject("sakha", forKey: "office")
dict.setObject("apple", forKey: "technology")
// Searching array
keysArray = ["name","city","office","tech"]
// returning Touple
var dictTouples = returnTouppleWithDictionary(dict, sortWithArray: keysArray)
print("\n\n Soreted as keys: \(dictTouples.allKeys) Values \(dictTouples.allValues) ")
// Touple
func returnTouppleWithDictionary(dict : NSDictionary, sortWithArray: NSArray) ->(allKeys:NSArray, allValues: NSArray){
var arrayKeys : NSMutableArray = NSMutableArray()
var arrayValues : NSMutableArray = NSMutableArray()
for i in 0..<keysArray.count{
var key : NSString = keysArray[i] as! String
var name: NSString? = dict.objectForKey(key) as! String;
arrayKeys.addObject(key)
arrayValues.addObject(name!)
}
return(arrayKeys,arrayValues)
}

Generate a complete list of key-value coding paths for nested NSDictionary's?

I have an NSDictionary that contains keys and values, and some values will also be NSDictionarys... to an arbitrary (but reasonable) level.
I would like to get a list of all valid KVC paths, e.g. given:
{
"foo" = "bar",
"qux" = {
"taco" = "delicious",
"burrito" = "also delicious",
}
}
I would get:
[
"foo",
"qux",
"qux.taco",
"qux.burrito"
]
Is there a simple way to do this that already exists?
You could recurse through allKeys. A key is a key path, obviously, and then if the value is an NSDictionary you can recurse and append.
- (void) obtainKeyPaths:(id)val intoArray:(NSMutableArray*)arr withString:(NSString*)s {
if ([val isKindOfClass:[NSDictionary class]]) {
for (id aKey in [val allKeys]) {
NSString* path =
(!s ? aKey : [NSString stringWithFormat:#"%#.%#", s, aKey]);
[arr addObject: path];
[self obtainKeyPaths: [val objectForKey:aKey]
intoArray: arr
withString: path];
}
}
}
And here is how to call it:
NSMutableArray* arr = [NSMutableArray array];
[self obtainKeyPaths:d intoArray:arr withString:nil];
Afterwards, arr contains your list of key paths.
Here is a Swift version I wrote after taking note from Matt's answer.
extension NSDictionary {
func allKeyPaths() -> Set<String> {
//Container for keypaths
var keyPaths = Set<String>()
//Recursive function
func allKeyPaths(forDictionary dict: NSDictionary, previousKeyPath path: String?) {
//Loop through the dictionary keys
for key in dict.allKeys {
//Define the new keyPath
guard let key = key as? String else { continue }
let keyPath = path != nil ? "\(path!).\(key)" : key
//Recurse if the value for the key is another dictionary
if let nextDict = dict[key] as? NSDictionary {
allKeyPaths(forDictionary: nextDict, previousKeyPath: keyPath)
continue
}
//End the recursion and append the keyPath
keyPaths.insert(keyPath)
}
}
allKeyPaths(forDictionary: self, previousKeyPath: nil)
return keyPaths
}
}

Resources