I have an array of objects that I convert to a NSSet:
NSArray *arr = #[#{ #"someProp": #21, #"unnecessaryProp": #"tada" }, ... ];
NSSet *collection = [NSSet setWithArray:arr];
I would like to project the properties I want (by key) out of each object in the set and end up with a new array like:
NSArray *projectedArray = [collection allObjects]; // #[#{ "someProp": #21 }, ... ], "unnecessaryProp" has been removed
Besides enumeration, is there any other way, perhaps NSPredicate?
NOTE: The objects in the array are subclasses of NSObject, in my example I mentioned a NSDictionary
Since NSPredicate does not do projections, you would end up enumerating the set. I would enumerate it with a block, and project the keys in the individual dictionaries like this:
NSArray *keep= #["someProp"];
NSMutableArray *res = [NSMutableArray array];
[collection enumerateObjectsUsingBlock:^(id dict, BOOL *stop) {
NSArray *values = [dict objectsForKeys:keep notFoundMarker:#""];
[res addObject:[NSDictionary dictionaryWithObjects:values forKeys:keep]];
}];
EDIT : (in response to comments)
I should have mentioned that the objects inside the array are subclasses of NSObject and objectsForKeys is not a method.
Then you could use MartinR's suggestion to build a dictionary using KVC:
NSArray *keep= #["someProp"];
NSMutableArray *res = [NSMutableArray array];
[collection enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
[res addObject:[obj dictionaryWithValuesForKeys:keep]];
}];
If you only need the values for one property of the objects in a collection of type NSSet or NSArray or their subclasses, you can use the KVC method valueForKey:
NSArray *dogs = #[#{#"name" : #"Fido",
#"toys" : #[#"Ball", #"Kong"]},
#{#"name" : #"Rover",
#"toys" : #[#"Ball", #"Rope"]},
#{#"name" : #"Spot",
#"toys" : #[#"Rope", #"Kong"]}];
NSArray *vals = [set valueForKey:#"name"];
NSLog(#"%#", vals);
The above code prints the following on the console:
2014-05-16 09:26:58.293 xctest[17223:303] (
Fido,
Rover,
Spot
)
If you need the values of several properties of the objects in the collection, use dictionaryWithValuesForKeys:. Given the same array as in the previous example, the following code...
NSDictionary *dict = [dogs dictionaryWithValuesForKeys:#[#"name", #"toys"]];
NSLog(#"%#", dict);
produces an array of dictionaries, and logs the following output:
2014-05-16 09:35:34.793 xctest[17275:303] {
name = (
Fido,
Rover,
Spot
);
toys = (
(
Ball,
Kong
),
(
Ball,
Rope
),
(
Rope,
Kong
)
);
}
This works regardless of whether the objects in the target collections are instances of NSDictionary or of custom classes.
you can use indexOfObjectPassingTest on your array or NSSet.
__block NSUInteger maxIdex = [_myArrray count]-1;
__block NSMutableIndexSet* objToRemove = [[NSMutableIndexSet alloc]init];
[_myArrray indexOfObjectPassingTest:^(id object, NSUInteger idx, BOOL * stop){
MyObject *obj = (MyObject*)object;
if(....){
[objToRemove addIndex:[_myArrray indexOfObject:obj]];
}
*stop = (idx == maxIdex);
return *stop;
}];
[_myArrray removeObjectsAtIndexes:objToRemove];
Related
If you have an array of dictionaries, how do I create a new array containing all the keys present for each dictionary in the array ?
NSArray *array = #[#{#"key1" : #"value 1"},
#{#"key2" : #"value 2"},
#{#"key3" : #"value 3"} ];
// how to achieve this?
NSArray *allKeys = #{#"key1", #"key2", #"key3"};
If you know that each element in the array is an NSDictionary, you can call the allKeys method on each item in the array. I've added a type check to this example in case your array contains other objects that are not NSDictionary:
NSArray *array = #[#{#"key1" : #"value 1"},
#{#"key2" : #"value 2"},
#{#"key3" : #"value 3"}];
NSMutableArray *allKeys = [#[] mutableCopy];
for (id obj in array) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = obj;
[allKeys addObjectsFromArray:[dict allKeys]];
}
}
NSLog(#"%#", allKeys);
Logs:
2016-04-20 11:38:42.096 ObjC-Workspace[10684:728578] (
key1,
key2,
key3
)
And if you need an immutable NSArray instead of an NSMutableArray:
NSArray *allKeysImmutable = [allKeys copy];
plz use this code, I think it helps you
NSArray *array = #[#{#"key1" : #"value 1"},
#{#"key2" : #"value 2"},
#{#"key3" : #"value 3"} ];
NSMutableArray *key_array=[NSMutableArray array];
for (NSDictionary *dictionary in array) {
NSArray *key_dictionary=[dictionary allKeys];
for (NSString *string_key in key_dictionary) {
[key_array addObject:string_key];
}
}
NSLog(#"%#",key_array);
Although Objective-C lacks an array-flattening method, you can nevertheless simplify the outer step:
NSMutableArray *result = [[NSMutableArray alloc] init];
for(NSArray *keys in [array valueForKey:#"allKeys"])
[result addObjectsFromArray:keys];
return [result copy];
Or, if you need keys deduplicated:
NSMutableSet *result = [[NSMutableSet alloc] init];
for(NSArray *keys in [array valueForKey:#"allKeys"])
[result unionSet:[NSSet setWithArray:keys]];
return [result allObjects];
... the only type assumption being (only slightly looser than) that array is all dictionaries. If you can annotate the collections any further then I recommend that you do.
I have a simple UITableView, when users adds new rows, these will be added to the NSMutableDictionary. I can retrieve the values for a specific key.
NSArray *myArr = [myDictionary valueForKey:#"Food"];
This will show me all values for key food, this is an example of my NSLog:
(
burger,
pasta )
If I add more objects to myDictionary but for a different key, for example:
NSArray *drinks = [NSArray arrayWithObjects:#"cola",#"sprite",nil];
[myDictionary setObject:drinks forKey:#"Drink"];
I can't retrieve all values using the following code:
NSArray *allMenu = [myDictionary allValues];
It shows me the following NSLog:
(
(
burger,
past
),
(
cola,
sprite
) )
I don't know where is the problem. Why I can't get all values from NSDictionary to NSArray.
If I use the code:
NSArray *allMenu = [[myDictionary allValues] objectAtIndex:0];
will show me the Food values. If I change objectAtIndex to 1 will show me the Drink value.
I am not entirely sure what you are asking, if you are trying to print all of the values within an NSDictionary do the following:
//Gets an array of all keys within the dictionary
NSArray dictionaryKeys = [myDictionary allKeys];
for (NSString *key in dictionaryKeys)
{
//Prints this key
NSLog(#"Key = %#", key);
//Loops through the values for the aforementioned key
for (NSString *value in [myDictionary valueForKey:key])
{
//Prints individual values out of the NSArray for the key
NSLog(#"Value = %#", value);
}
}
You can do this in one line by flattening the returned 2-dimensional array by using key value coding (KVC). I found this in another answer, see the docs. In your case, it looks as follows:
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionary];
NSArray *food = [NSArray arrayWithObjects:#"burger",#"pasta",nil];
[myDictionary setObject:food forKey:#"Food"];
NSArray *drinks = [NSArray arrayWithObjects:#"cola",#"sprite",nil];
[myDictionary setObject:drinks forKey:#"Drink"];
NSArray *allMenue = [[myDictionary allValues] valueForKeyPath:#"#unionOfArrays.self"];
Try this Solution :
- (NSDictionary *) indexKeyedDictionaryFromArray:(NSArray *)array
{
id objectInstance;
NSUInteger indexKey = 0U;
for (objectInstance in myArr)
[mutableDictionary setObject:objectInstance forKey:[NSNumber numberWithUnsignedInt:indexKey++]];
return (NSDictionary *)[myDictionary autorelease];
}
I have an array having objects with different properties. I want to create an array of sets which contain objects with same value of a single property of the object.
Suppose this is an array of object which has property a and b
1: {a:10, b:5}, 2: {a:2,b:5}, 3: {a:20,b:5}, 4: {a:5,b:5}, 5: {a:4,b:20}, 6: {a:51,b:20}
I want to create another array of NSSet of objects with distinct values of property b
so the result would be the following Array of 2 NSSet
1: {a:10, b:5}, {a:2,b:5}, {a:20,b:5}, {a:5,b:5}
2: {a:4,b:20}, {a:51,b:20}
How can this be done?
I'd do this by first creating a dictionary of sets where the keys of the dictionary are the unique values of "b".
Note: This is untested code. There could be typos here.
NSArray *objectArray = ... // The array of "SomeObject" with the "a" and "b" values;
NSMutableDictionary *data = [NSMutableDictionary dictionary];
for (SomeObject *object in objectArray) {
id b = object.b;
NSMutableSet *bSet = data[b];
if (!bSet) {
bSet = [NSMutableSet set];
data[b] = bSet;
}
[bSet addObject:object];
}
NSArray *setArray = [data allValues];
setArray will contain your array of sets.
This codes also assumes you have a sane isEqual: and hash implementation on your SomeObject class.
This is how you can do this:
NSArray *data = #[#{#"a":#10, #"b":#5}, #{#"a":#2,#"b":#5}, #{#"a":#4,#"b":#20}, #{#"a":#51,#"b":#20}];
NSSet *bSet = [NSSet setWithArray: [data valueForKey: #"b"]];
NSMutableArray *filteredArray = [[NSMutableArray alloc] initWithCapacity: bSet.count];
for (NSNumber *bValue in bSet) {
NSPredicate *anArrayFilterPredicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *aDictionaryData, NSDictionary *bindings) {
if ([aDictionaryData[#"b"] isEqual:bValue]) {
return YES;
}
return NO;
}];
NSArray *uniqueBValueArray = [data filteredArrayUsingPredicate:anArrayFilterPredicate];
[filteredArray addObject:uniqueBValueArray];
}
NSLog(#"filteredArray = %#", filteredArray);
Using Key-Value coding collection operators, we can get the array of distinct values for an object. Then you could easily compute the results you want.
In your case this could be done like this.
NSArray *arrayOfDistinctObjects = [array valueForKeyPath:#"#distinctUnionOfObjects.b"];
NSMutableArray *newSetArray = [NSMutableArray new];
for (id value in arrayOfDistinctObjects) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.b == %#", value];
NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
NSSet *newSet = [NSSet setWithArray:filterArray];
[newSetArray addObject:newSet];
}
where array is the array of objects on which you wanna operate.
arrayOfDistinctObjects gives you the array of distinct values for b.
I have an NSArray containing multiple NSDictionary fetching from the server, and i want to iterate every NSDictionary one of the key's value called "id" and then add them to a NSMutableArray. I used "for loop" to implement this function, but the results of the NSMutableArray log in Xcode debug area was very weird and incorrectly.
This is the code for iterate every NSDictionary in a NSArray.
//Get the dictionary that included item ids.
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:&error];
//Create the array add item id objects.
NSMutableArray *itemIds = [NSMutableArray array];
//Fetching item ids in dictionary array.
NSArray *itemIdsDictionary = dictionary[#"itemRefs"];
NSLog(#"%i", [itemIdsDictionary count]);//Have valid dictionary.
for(int i = 0; i < [itemIdsDictionary count]; i++)
{
NSString *aId = (itemIdsDictionary[i])[#"id"];
[itemIds addObject:aId];
NSLog(#"%#", itemIds);
NSLog(#"%#", aId);
}
the NSMutableArray log in Xcode degug area is:
2013-07-04 22:55:26.053 Readable[3222:c07] 5
2013-07-04 22:55:26.053 Readable[3222:c07] (
51d58c198e49d061db0011e3
)
2013-07-04 22:55:26.054 Readable[3222:c07] (
51d58c198e49d061db0011e3,
51d58c198e49d061db0011e4
)
2013-07-04 22:55:26.054 Readable[3222:c07] (
51d58c198e49d061db0011e3,
51d58c198e49d061db0011e4,
51d5745982d493d61500e706
)
2013-07-04 22:55:26.054 Readable[3222:c07] (
51d58c198e49d061db0011e3,
51d58c198e49d061db0011e4,
51d5745982d493d61500e706,
51d5745982d493d61500e707
)
2013-07-04 22:55:26.054 Readable[3222:c07] (
51d58c198e49d061db0011e3,
51d58c198e49d061db0011e4,
51d5745982d493d61500e706,
51d5745982d493d61500e707,
51d55fb04de3837bf3006e83
)
Really hope someone can help me solve this problem, and this is very important phase in my own app. Thanks!!!!!!
Try this -
for(int i = 0; i < [itemIdsDictionary count]; i++)
{
NSDictionary *dictionary = [itemIdsDictionary objectAtIndex:i];
[itemIds addObject:[dictionary objectForKey:#"id"]];
}
NSLog(#"itemIds : %#",itemIds);
Try
for (NSDictionary *dict in itemIdsDictionary)
[itemIds addObject:[dict valueForKey#"id"]];
It would also help if you gave the structure of your dictionary. What keys are returned from the web service? ect.
//Get the dictionary that included item ids.
NSDictionary *dictionary = [NSDictionary dictionary];
//Create the array to hold the ID's
NSMutableArray *itemIds = [NSMutableArray array];
//Fetching item ids in dictionary array.
NSArray *itemIdsDictionary = dictionary[#"itemRefs"];
[itemIdsDictionary enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
[itemIds addObject:[dict objectForKey:#"id"]];
}];
Here is an example of what I think you are tying to do.
If you want to make sure you only have one entry of an ID even if there are duplicates in your dictionary from the web service you can use a NSMutableSet to stop this. Below is the example of both.
//This is just an array to hold the dictionaries that you should get from the web service
NSMutableArray *arrayOfDictionariesFromWebService = [NSMutableArray array];
//Make some fake data for the example
for (int i = 0; i < 5; ++i)
{
//Make a dictionary with a value for the key id
NSDictionary *dictionary = #{#"id" : [NSString stringWithFormat:#"ID%d", i]};
//Add it to the array
[arrayOfDictionariesFromWebService addObject:dictionary];
}
//Add a duplicate item to show example of using a set
NSDictionary *duplicateObject = #{#"id" : #"ID0"};
[arrayOfDictionariesFromWebService addObject:duplicateObject];
//Create an array and a set to hold the ID's
NSMutableArray *itemIds = [NSMutableArray array];
NSMutableSet *setOfItemIds = [NSMutableSet set];
//Enumerate through the array of dictionaries to pull out the ID from the dictionary and add it to the array and set of IDs
[arrayOfDictionariesFromWebService enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
[itemIds addObject:[dict objectForKey:#"id"]];
[setOfItemIds addObject:[dict objectForKey:#"id"]];
}];
//If you want to sort your ID's you can sort it using sortedArrayUsingDescriptors
NSArray *sortedArrayOfItemIDs = [[setOfItemIds allObjects] sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"description" ascending:YES]]];
//Log out the result
NSLog(#"Array of ID's \n%#", itemIds);
NSLog(#"Array of ID's from the set \n%#", [setOfItemIds allObjects]);
NSLog(#"Sorted array of ID's from the set \n%#", sortedArrayOfItemIDs);
The console should look like this.
2013-07-10 09:04:40.824 STC Companion[574:c07] Array of ID's
(
ID0,
ID1,
ID2,
ID3,
ID4,
ID0
)
2013-07-10 09:04:40.825 STC Companion[574:c07] Array of ID's from the set
(
ID2,
ID3,
ID0,
ID4,
ID1
}
2013-07-10 09:13:08.063 STC Companion[886:c07] Sorted array of ID's from the set
(
ID0,
ID1,
ID2,
ID3,
ID4
)
If you have an NSMutableArray with three NSDictionarys like this:
{
name:steve, age:40;
name:steve, age:23;
name:paul, age:19
}
How do I turn that into an array with just two strings { steve, paul }. In other words, the unique names from the original NSMutableArray? Is there a way to do this using blocks?
Similar to the other answer, you could also do:
NSSet * names = [NSSet setWithArray:[myArray valueForKey:#"name"]];
Or
NSArray * names = [myArray valueForKeyPath:#"#distinctUnionOfObjects.name"];
something like that:
NSMutableSet* names = [[NSMutableSet alloc] init];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)) {
[names addObject:[obj valueForKey:#"name"]];
}];
[names allObjects] will return a NSArray of unique name