Creating NSDictionary with keys that have multiple values - objective-c

I have a mutable array that contains NSDictionary dic1 objects,
each dictionary has a key called contactId, more than one dictionary can have the same value for contactId.
What I want to do is to create an NSDictionary with unique contactIds as the keys and an array value that contains a list of all NSDictionary dic1 objects that have the value contactId equal to the key.
How can I do this?
My data looks like this:
**myArray**:[ **dic1** {contactId = x1 , name = name1 }, **dic2**{contactId = x2, name =
name2 }, **dic3**{contactId = x1, name = name3} ]
I want it to become like this:
**NSDictionary**: { **x1**:[dic1, dic3], **x2**:[dic2] }

Use fast enumeration:
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (id obj in myArray)
{
NSString *contactId = [obj objectForKey:#"contactId"];
NSMutableSet *contacts = [result objectForKey:contactId];
if (!contacts)
{
contacts = [NSMutableSet set]
[result setObject:contacts forKey:contactId];
}
[contacts addObject:obj];
}
You could use blocks for no real added benefit:
__block NSMutableDictionary *result = [NSMutableDictionary dictionary];
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSString *contactId = [obj objectForKey:#"contactId"];
NSMutableSet *contacts = [result objectForKey:contactId];
if (!contacts)
{
contacts = [NSMutableSet set]
[result setObject:contacts forKey:contactId];
}
[contacts addObject:obj];
}];

How about the classic way?
NSMutableDictionary* Result;
NSEnumerator* Enumerator;
NSDictionary* Dict;
Result=[[NSMutableDictionary alloc] init];
Enumerator=[YourArray objectEnumerator];
while ((Dict=[Enumerator nextObject])!=nil)
{
NSString* ContactID;
NSMutableSet* Contacts;
ContactID=[Dict objectForKey:#"contactID"];
Contacts=[Result objectForKey:ContactID];
if (Contacts==nil)
{
Contacts=[[NSMutableSet alloc] init];
[Result setObject:Contacts forKey:ContactID];
[Contacts release];
}
[Contacts addObject:Dict];
}
This should create a Result dictionary. I haven't tested (or even compiled) this, though.

Related

NSArray and NSDictionary

I have NSDictionaries in NSArray just like below.
array(dictionary("user":1, "p1":1), dictionary("user":2, "p1":3),
dictionary("user":1, "p1":5), dictionary("user":2, "p1":7))
And I want to turn this array into dictionary like below.
NSArray *u1 = [NSArray arrayWithObjects:#"1", #"5", nil];
NSArray *u2 = [NSArray arrayWithObjects:#"3", #"7", nil];
keys = [NSArray arrayWithObjects:#"u1", #"u2", nil];
points = [NSDictionary dictionaryWithObjectsAndKeys:u1, #"u1", u2, #"u2", nil];
How can I do that? I am lost, can you guys please help me?
Couldn't you just iterate over your original array, asking each dictionary if the object for key "user" is 1, and if so, copy the object into a new array at index 0? Or if your user numbers are in counting order, maybe even have the index number equal the user number. Then repeat for "user" = 2, etc. Then make a dictionary so that each key/object pair is created by keys from the keys array (keys[i]) and objects from your new array (objects[i]).
What have you tried?
Here is some code typed directly into the answer, so it has not be tested:
You haven't given a name for your original array, so let's assume it is:
NSArray *originalArray;
We need a mutable dictionary to store the result:
NSMutableDictionary *points = [NSMutableDictionary new];
Now we need to process every element in the original array and it is a dictionary:
for(NSDictionary *item in originalArray)
{
Get the current entry in points array that matches item. You don't give types for your entries, so we'll use id:
id currentUser = [item objectForKey:#"user"];
NSMutableArray *currentValues = [points objectForKey:currentUser];
If this is the first occurrence of currentUser then currentValues will be nil, and we need to create an array for the p1 value and add it to points:
if (currentValues == nil)
[points addObject:[NSMutableArray arrayWithObject:[item objectForKey:#"p1"]
forKey:currentUser
]
]
Otherwise we just add the p1 value to the array:
else
[currentValues setObject:[item objectForKey:#"p1"]];
close out the loop and get the keys:
}
NSArray *keys = [points allKeys];
Now if you're using Xcode 4.5 you can use modern syntax for some of that:
NSMutableDictionary *points = [NSMutableDictionary new];
for(NSDictionary *item in originalArray)
{
id currentUser = item[#"user"];
NSMutableArray *currentValues = points[currentUser];
if (currentValues == nil)
points[currentUser] = [NSMutableArray arrayWithObject:item[#"p1"];
else
[currentValues addObject:item[#"p1"]];
}
NSArray *keys = [points allKeys];
HTH
Another possible solution (works with an arbitrary number of users):
NSArray *orig = #[
#{#"user" : #"1", #"p1" : #"1"},
#{#"user" : #"2", #"p1" : #"3"},
#{#"user" : #"1", #"p1" : #"5"},
#{#"user" : #"2", #"p1" : #"7"},
];
// Create set of all users (without duplicates)
NSSet *users = [NSSet setWithArray:[orig valueForKey:#"user"]];
NSMutableDictionary *points = [NSMutableDictionary dictionary];
for (NSString *user in users) {
// newKey = "u" + username, e.g. "u1" or "u2":
NSString *newKey = [#"u" stringByAppendingString:user];
// newValue = array of "p1" values of the current user:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"user == %#", user];
NSArray *newValue = [[orig filteredArrayUsingPredicate:pred] valueForKey:#"p1"];
// Add to dictionary:
[points setObject:newValue forKey:newKey];
}
NSLog(#"%#", points);
Output:
{
u1 = (
1,
5
);
u2 = (
3,
7
);
}
And the keys can be obtained by
NSArray *keys = [points allKeys];
You can do, like this (code not tested)
NSMutableArray *keys=[NSMutableArray new];
NSMutableArray *u1=[NSMutableArray new];
NSMutableArray *u2=[NSMutableArray new];
NSMutableDictionary *points=[NSMutableDictionary new];
for (id dict in array){
NSString *user=[dict objectForKey:#"user"];
NSString *p1=[dict objectForKey:#"p1"];
[keys addObject:[NSString stringWithFormat:#"%#",user]];
if( [user isEqualToString:#"1"] ){
[u1 addObject:user];
}
else{
[u2 addObject:user];
}
}
points=[NSDictionary dictionaryWithObjectsAndKeys:u1,#"u1",u2, #"u2", nil];
Tons of approaches. Here's another:
NSArray *originalArray = #[
#{#"user":#"u1", #"p1":#"1"},
#{#"user":#"u2", #"p1":#"3"},
#{#"user":#"u1", #"p1":#"5"},
#{#"user":#"u2", #"p1":#"7"}
];
NSLog(#"originalArray = %#", originalArray);
NSMutableDictionary *results = [NSMutableDictionary dictionary];
for (NSDictionary *dictionary in originalArray) {
NSString *user = dictionary[#"user"];
NSString *p1 = dictionary[#"p1"];
if (!results[user])
results[user] = [NSMutableArray array];
[results[user] addObject:p1];
}
NSLog(#"results = %#", results);
That takes:
originalArray = (
{
p1 = 1;
user = u1;
},
{
p1 = 3;
user = u2;
},
{
p1 = 5;
user = u1;
},
{
p1 = 7;
user = u2;
}
)
And gives
results = {
u1 = (
1,
5
);
u2 = (
3,
7
);
}

How to extract unique objects from multiple arrays

EDIT:
I have two different arrays with some repeated strings and i want to create a new array with the only the unique strings.
For instance, take these two arrays:
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Result should be an array with objects "b", and "d"
// since they are the only two that are not repeated in the other array.
EDIT:
// Your starting arrays
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Create two new arrays that only contain the objects
// which are not in the other array:
NSMutableArray *uniqueElementsInArray1 = [array1 mutableCopy];
[uniqueElementsInArray1 removeObjectsInArray:array2];
NSMutableArray *uniqueElementsInArray2 = [array2 mutableCopy];
[uniqueElementsInArray2 removeObjectsInArray:array1];
// Combine the two arrays.
// Result contains objects #"b" and #"d":
NSArray *result = [uniqueElementsInArray1 arrayByAddingObjectsFromArray:uniqueElementsInArray2];
For this you just declare one another temp NSMutableArray . Retrieve whatever data u have from your original array say objectArray. Check whether the temp array have that or not and put it into the temp array. Just refer following code:
for(NSString *str in objectArray)
{
if(![tempArray containsObject:str])
{
[tempArray addObject:str];
}
}
After this u can continue to use tempArray or put tempArray into objectArray if you want to use objectArray further.I think this should work for you.
You can use NSSet as a filter (think of Venn Diagrams in your head):
NSArray *array1 = #[#1,#2,#3,#4,#2,#3];
NSArray *array2 = #[#3,#4,#5,#6,#4,#6];
NSSet *set1 = [NSSet setWithArray:array1]; // [1,2,3,4]
NSSet *set2 = [NSSet setWithArray:array2]; // [3,4,5,6]
METHOD 1 (my favorite):
NSMutableSet *mSet1 = [set1 mutableCopy];
NSMutableSet *mSet2 = [set2 mutableCopy];
[mSet1 minusSet:set2]; // mSet1 = [1,2]
[mSet2 minusSet:set1]; // mSet2 = [5,6]
[mSet1 unionSet:mSet2]; // mSet1 = [1,2,5,6], only the unique elements.
// Now just put it in an immutable collections with a self-docu name...
NSArray *arrayOfUniqueness = [setOfUniqueElementsOnly allObjects];
METHOD 2 (more explicit test, no need for Venn Diagrams):
NSSet *setOfObjsUniqueTo1 = [set1 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set2 containsObject:obj];
}]; // [1,2]
NSSet *setOfObjsUniqueTo2 = [set2 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set1 containsObject:obj];
}]; // [5,6]
NSMutableSet *oneSetToRuleThemAll = [NSMutableSet setWithSet:setOfObjsUniqueTo1];
// [1,2]
[oneSetToRuleThemAll unionSet:setOfObjsUniqueTo2]; // [1,2,5,6]
// Or as an array:
NSArray *anotherArrayOfUniqueness = [oneSetToRuleThemAll allObjects];
METHOD 3 (eschews NSSet, but I would not seat this code opposite the Queen of England at a formal dinner -- it is inelegant):
NSMutableArray *mArray1 = [NSMutableArray new];
NSMutableArray *mArray2 = [NSMutableArray new];
NSIndexSet *uniqueIndexes1 = [array1 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array2 containsObject:obj];
}]; // [0,1,4] (b/c #1 and #2 are unique to array1)
[uniqueIndexes1 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray1 addObject:array1[idx]];
}]; // #[#1,#2,#2]
NSIndexSet *uniqueIndexes2 = [array2 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array1 containsObject:obj];
}]; // [2,3,5] (b/c #5 and #6 are unique to array2)
[uniqueIndexes2 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray2 addObject:array2[idx]];
}]; // #[#5,#6,#6]
NSArray *unionArray = [array1 arrayByAddingObjectsFromArray:array2];
// #[#1,#2,#2,#5,#6,#6]
NSArray *yetAnotherArrayOfUniqueness = [[NSSet setWithArray:unionArray] allObjects];
// #[#1,#2,#5,#6]
Not the questioner's question, but to get an array with duplicates removed (i.e., where each element is unique), similar magic can be done:
//given...
NSArray *arr1 = #[#"a", #"b", #"c"];
NSArray *arr2 = #[#"b", #"c", #"d"];
//...make a single array to rule them all:
NSArray *temp = [arr1 arrayByAddingObjectsFromArray:arr2];
//[a,b,c,b,c,d]
//Make an NSSet from the two:
NSSet *filterSet = [NSSet setWithArray:temp]; // Set has: a,b,c,d
//Finally, transmogrify that NSSet into an NSArray:
NSArray *arrayOfUniqueness = [filterSet allObjects]; // [a,b,c,d]
As per the Apple Docs (emphasis added):
+setWithArray:
Creates and returns a set containing a uniqued collection of the objects contained in a given array.
UPDATE: And see here for a similar question: Remove all strings with duplicates in an NSArray
use Set as a filter, example:
String[] arr = {"a","a","b"};
Object[] uniqueArr = (Object[])new HashSet<String>(Arrays.asList(arr)).toArray();

FMDB Query with dictionary

I need to run a query that looks would look like
INSERT INTO Appointments (field1, field2, field3, ..., field30) VALUES (value1, value2, value3, ..., value30)
I have my Appointments being stored inside a Dictionary and would like to loop through that dictionary to make the keys equal the fields and the values equal the values.
I'm trying to use the executeUpdate:... withParameterDictionary:... but can't figure out how to make that work with multiple fields if I don't know the field names. The field names are being sent via JSON and instead of manually typing out 30 fields I would just like to loop through the dictionary and get them that way.
I have even tried
NSMutableArray *keys = nil;
NSMutableArray *values = nil;
for (NSDictionary *dict in [json objectForKey:#"data"]) {
keys = [NSMutableArray array];
values = [NSMutableArray array];
for (id key in dict) {
[keys addObject:key];
[values addObject:[NSString stringWithFormat:#":%#", key]];
}
NSString *keyString = [keys componentsJoinedByString:#","];
NSString *valueString = [values componentsJoinedByString:#","];
[[dataObj db] executeUpdate:#"DELETE FROM Appointments"];
NSLog(#"INSERT INTO Appointments (%#) VALUES (%#)", keyString, valueString);
[[dataObj db] executeUpdate:#"INSERT INTO Appointments (?) VALUES (?)", keyString, valueString];
}
The code above prints the NSLog how the query should looks but nothing is being inserted into the database. I know this because I am opening the simulator database file after the queries run and it is still blank.
How can I get the above code to work or how can I get the executeQuery:... withParameterDictionary:... to work with multiple names.
I ran a couple of quick tests, and this works for me:
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:#"AAAA44", #"a", #"BBBB44", #"b", #"CCCC44", #"c", nil];
NSMutableArray* cols = [[NSMutableArray alloc] init];
NSMutableArray* vals = [[NSMutableArray alloc] init];
for (id key in dict) {
[cols addObject:key];
[vals addObject:[dict objectForKey:key]];
}
NSMutableArray* newCols = [[NSMutableArray alloc] init];
NSMutableArray* newVals = [[NSMutableArray alloc] init];
for (int i = 0; i<[cols count]; i++) {
[newCols addObject:[NSString stringWithFormat:#"'%#'", [cols objectAtIndex:i]]];
[newVals addObject:[NSString stringWithFormat:#"'%#'", [vals objectAtIndex:i]]];
}
NSString* sql = [NSString stringWithFormat:#"insert into test (%#) values (%#)", [newCols componentsJoinedByString:#", "], [newVals componentsJoinedByString:#", "]];
NSLog(#"%#", sql);
BOOL updateSuccess = [db executeUpdate:sql];
The trick is to add ' to the data in the arrays.
NSDictionary *argsDict
= [NSDictionary dictionaryWithObjectsAndKeys:#"My Name",
#"name", nil];
[db executeUpdate:#"INSERT INTO myTable (name) VALUES (:name)"
withParameterDictionary:argsDict];
Here is some sample code I just wrote to support optional values at insert time. Just briefly tested but I think it works.
NSMutableDictionary* fieldsandvalues = [NSMutableDictionary dictionary];
fieldsandvalues[#"word"] = userphrase.word;
fieldsandvalues[#"translation"] = userphrase.translation;
if (userphrase.samplesentence.length > 0) {
fieldsandvalues[#"samplesentence"] = userphrase.samplesentence;
}
if (userphrase.notes.length > 0) {
fieldsandvalues[#"notes"] = userphrase.notes;
}
NSMutableArray* keyswithcolon = [NSMutableArray array];
for (NSString* key in fieldsandvalues.allKeys) {
[keyswithcolon addObject:[NSString stringWithFormat:#":%#", key]];
}
NSString* sql = [NSString stringWithFormat:#"INSERT INTO userphrase (%#) VALUES (%#)", [fieldsandvalues.allKeys componentsJoinedByString:#","], [keyswithcolon componentsJoinedByString:#","]];
// DLog(#"sql: %#", sql);
if (![self.db executeUpdate:sql withParameterDictionary:fieldsandvalues]) {
NSAssert(NO, #"Failed inserting userphrase into database! Last error: %# - %#", self.db.lastError, self.db.lastErrorMessage);
return nil;
}

Get matched string from two NSArrays

How can I save the string that match from one NSArray with one index difference in NSMutableArray?
For example, there are three "apple", four "pineapple", six "banana", two "cocoa" and the rest of words dont have duplicate(s) in the nsarray, i would like to know if the nsarray has at least two same words. If yes, I would like to save "apple", "pineapple, "banana" and "cocoa" once in nsmutablearray. If there are other alike words, I would like to add them to namutablearray too.
My code (which still doesn't work properly);
NSArray *noWords = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"words" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL]
componentsSeparatedByString:#"\n"]];
NSUInteger scount = [noWords count];
int ii = 0;
NSString *stringline;
for (ii; ii < scount; ii++)
{
stringline = [noWords objectAtIndex:ii];
NSLog(#"stringline : %# ", stringline);
}
int i = 1;
NSString *line;
for (i ; i < 10; i++)
{
line = [noWords objectAtIndex:i];
NSLog (#"line : %# ", line);
NSMutableArray *douwords = [NSMutableArray array];
if ([stringline isEqualToString:line])
{
NSString *newword;
for (newword in douwords)
{
[douwords addObject:newword];
NSLog (#"detected! %# ", douwords);
}
}
}
Here's a solution using two sets:
- (NSArray *)getDuplicates:(NSArray *)words
{
NSMutableSet *dups = [NSMutableSet set],
*seen = [NSMutableSet set];
for (NSString *word in words) {
if ([seen containsObject:word]) {
[dups addObject:word];
}
[seen addObject:word];
}
return [dups allObjects];
}
Assuming NSSet uses hash tables behind the scenes (which I'm betting it does), this is going to be faster than the previously suggested O(n^2) solution.
Here's something off the top of my head:
NSMutableSet* duplicates = [NSMutableSet set];
NSArray* words = [NSArray arrayWithObjects:#"Apple", #"Apple", #"Orange", #"Apple", #"Orange", #"Pear", nil];
[words enumerateObjectsUsingBlock:^(NSString* str, NSUInteger idx, BOOL *stop) {
for (int i = idx + 1; i < words.count; i++) {
if ([str isEqualToString:[words objectAtIndex:i]]) {
[duplicates addObject:str];
break;
}
}
}];
NSLog(#"Dups: %#", [duplicates allObjects]); // Prints "Apple" and "Orange"
The use of an NSSet, as opposed to an NSArray, ensures strings are not added more than once. Obviously, there are optimizations that could be done, but it should be a good starting point.
I assume that you want to count appearances of words in your array and output those with a count of more than one. A basic and verbose way to do that would be:
// Make an array of words - some duplicates
NSArray *wordList = [[NSArray alloc] initWithObjects:
#"Apple", #"Banana", #"Pencil",
#"Steve Jobs", #"Kandahar",
#"Apple", #"Banana", #"Apple",
#"Pear", #"Pear", nil];
// Make an mutable dictionary - the key will be a word from the list
// and the value will be a number representing the number of times the
// word appears in the original array. It starts off empty.
NSMutableDictionary *wordCount = [[NSMutableDictionary alloc] init];
// In turn, take each word in the word list...
for (NSString *s in wordList) {
int count = 1;
// If the word is already in the dictionary
if([wordCount objectForKey:s]) {
// Increse the count by one
count = [[wordCount objectForKey:s] intValue] + 1;
}
// Save the word count in the dictionary
[wordCount setObject:[NSNumber numberWithInt:count] forKey:s];
}
// For each word...
for (NSString *s in [wordCount keysOfEntriesPassingTest:
^(id key, id obj, BOOL *stop) {
if ([obj intValue] > 1) return YES; else return NO;
}]) {
// print the word and the final count
NSLog(#"%2d %#", [[wordCount objectForKey:s] intValue], s);
}
The output would be:
3 Apple
2 Pear
2 Banana

how to remove an NSMutableArray object by key?

i have structured an NSMutableArray and here is an example
(
{
Account = A;
Type = Electricity;
},
{
Account = B;
Type = Water;
},
{
Account = C;
Type = Mobile;
} )
when i try to delete Account B using
[data removeObject:#"B"];
Nothing Happens
[[NSUserDefaults standardUserDefaults] synchronize];
NSArray *archivedArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
if (archivedArray == nil) {
data = [[NSMutableArray alloc] init];
} else {
data = [[NSMutableArray alloc] initWithArray:archivedArray];
}
If you're actually using an array and not a dictionary, you need to search for the item before you can remove it:
NSUInteger index = [data indexOfObjectPassingTest:^BOOL (id obj, NSUInteger idx, BOOL *stop) {
return [[(NSDictionary *)obj objectForKey:#"Account"] isEqualToString:#"B"];
}];
if (index != NSNotFound) {
[data removeObjectAtIndex:index];
}
Alternative: try a NSMutableDictionary:
NSArray *accounts = [NSArray arrayWithObjects:#"A", #"B", #"C", nil];
NSArray *types = [NSArray arrayWithObjects:#"Electricity", #"Water", #"Mobile", nil];
NSMutableDictionary* data = [NSMutableDictionary dictionaryWithObjects:types forKeys:accounts];
[data removeObjectForKey:#"B"];
An NSArray is like a list of pointers, each pointer points to an object.
If you call:
[someArray removeObject:#"B"];
You create a new NSString object that contains the string "B". The address to this object is different from the NSString object in the array. Therefore NSArray cannot find it.
You will need to loop through the array and determine where the object is located, then you simply remove it by using removeObjectAtIndex: