Unique items from a NSMutableArray with NSDictionary items? - ios

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

Related

Find index of all duplicate elements in NSArray

It's hard to explain why I need index of duplicate elements in array. When I tried to fetch the index of element in traditional way it shows only one index, but I need to fetch the all index of duplicate values
for ex:
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
int index = [array indexOfObject:element];
NSLog(#"index %d",index);
here if I try to fetch index of " one " it shows index is 0 but I need to get further indexes of one
You can fetch the index of duplicates like this:
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj isEqualToString:#"one"])
{
NSLog(#"index %d",idx);
}
}];
int i,count=0;
for (i = 0; i < [array count]; i++) {
if element == [array objectAtIndex:i] {
indices[count++] = i;
}
}
Declare an empty array indices, and indices will contain all the indices of the given element.
NSString *element = #"one";
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
NSIndexSet *matchingIndexes = [array indexesOfObjectsPassingTest:^BOOL(NSString *obj, NSUInteger idx, BOOL *stop) {
return [obj isEqual:element];
}];
[matchingIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSLog(#"%ld", (long)idx);
}];
Ultimately I don't think the NSArray methods are going to help you here, so you're going to have to write some pretty basic code. There is probably a cleaner answer, but here is a fairly simply solution to the problem.
This just goes through the array, and creates an NSDictionary for each unique number. It assumes the array is sorted as your example was, so simply checks the prior index's value against the current index to see if they have changed. When they change, it knows it's done with that value and saves the dictionary to an array.
NSArray *array=#[#"one",#"one",#"one",#"two",#"two",#"four",#"four",#"four"];
NSString *priorString = array[0];
NSMutableDictionary *duplicatesByKey = [[NSMutableDictionary alloc] init];
NSMutableArray *indexesOfDuplicates = [[NSMutableArray alloc] init];
int index = 0;
for (NSString *string in array) {
if ([priorString isEqualToString:string]) {
[indexesOfDuplicates addObject:[NSNumber numberWithInt:index]];
} else {
[duplicatesByKey setObject:indexesOfDuplicates forKey:priorString];
indexesOfDuplicates = [[NSMutableArray alloc] init];
[indexesOfDuplicates addObject:[NSNumber numberWithInt:index]];
}
priorString = string;
index ++;
}
[duplicatesByKey setObject:indexesOfDuplicates forKey:priorString];
I hope that helps.
Use
NSCountedSet * countedSet = [NSCountedSet setWithArray: array];
and
NSSet * uncountedSet = [NSSet setWithArray: array];
-- to create a counted set from your array, and a conventional NSSet.
Then:
[countedSet minusSet: uncountedSet];
countedSet will now contain only elements for the duplicates (if any), and the countForObject: method will return the number of duplicates (in excess of 1) for that element.

Remove duplicates from large NSMutableArray

I have a large mutable array with lots of duplicate values in alphabetical order.
I need to be able to convert my array *Array into a new array that contains one entry for each string variant.
I am currently using:
NSArray *array = [NSArray arrayWithObjects:papersObject.paperSubject, nil];
NSCountedSet *paperSet = [[NSCountedSet alloc] initWithArray:array];
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
[namesSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop){
if ([paperSet countForObject:obj] == 1) {
[namesArray addObject:obj];
}
}];
NSLog(#"%#", namesArray);
But this returns a long list of the same array, still with duplicates.
Any ideas?
What about:
NSArray *arrayWithNoDuplicates = [[NSSet setWithArray:papersObject.paperSubject] allObjects];
A. What is namesSet? paperSet?
B. However:
NSOrderedSet *set = [NSOrderedSet orderedSetWithArray:array];
NSArray *arrayWithUniquesIsAnOrderedSet = set.array;
BTW: I would highly recommend to use an ordered set instead of an array, because an array with unique objects is an ordered set.

Projection of properties of objects in NSArray/NSSet collections

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];

Best way for getting the indexes of matched objects in NSArray in iOS?

I have the following two arrays.
NSArray *array1=[[NSArray alloc]initWithObjects:#"ABC",#"DEF", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"ABC",#"123",#"DEF",#"DEF", nil];
Now i have to search each array1's object and in array2 and need to get the matched indexes.
And my application contains more than one thousand objects in array2.
Please suggest the best possible way other than putting second for loop in first for loop
for (int i=0; i<array1.count; i++)
{
//Need to search the [array1 objectAtIndex:i] string in array2 and need to get the matched indexes into an array in best optimised way here.
NSMutableArray *matchedIndexesArray=[[NSMutableArray alloc]init];
NSString *stringToSearch=[array1 objectAtIndex:i];
//here i can put another array like below to get the matched indexes..but is there any optimized way other than this for loop here? or is there any simple inbuilt method to get the matched objects into an array here.
for (int j=0; j<array2.count; j++)
{
if ([stringToSearch isEqualToString:[array2 objectAtIndex:j]])
{
[matchedIndexesArray addObject:[NSString stringWithFormat:#"%d",j]];
}
}
NSLog(#"matchedIndexesArray-->%#<--",matchedIndexesArray);
//I will use this matchedIndexesArray here further processing...
//
//
//
//Large Code Here
//
//
//
}
According to the NSSet documentation, membership testing is faster for sets than for arrays.
Therefore it makes sense to convert array1 to a set first:
NSSet *set1 = [NSSet setWithArray:array1];
and then test each object of array2 for membership in the set. This can be conveniently
done as
NSIndexSet *matchingIndexes = [array2 indexesOfObjectsPassingTest:^BOOL(NSString *obj, NSUInteger idx, BOOL *stop) {
return [set1 containsObject:obj];
}];
Show all matching indexes:
[matchingIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSLog(#"%ld", (long)idx);
}];
// Output: 0, 2, 3
UPDATE: (after question edit) No, there is no method to fill an NSArray with the indices of matching objects. But there is a method to fill an NSIndexSet. NSIndexSet is a special collection to store indices
into some other data structure, such as an array. Then your code would look like
for (NSString *stringToSearch in array1) {
NSIndexSet *matchingIndexes = [array2 indexesOfObjectsPassingTest:^BOOL(NSString *obj, NSUInteger idx, BOOL *stop) {
return [stringToSearch isEqualToString:obj];
}];
NSLog(#"matchingIndexes: %#", matchingIndexes);
// Work with matchingIndex, for example enumerate all indices:
[matchingIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSLog(#"%ld", (long)idx);
}];
}
But I do not know if it makes much difference in performance.
NSArray *a = #[#"123", #"456", #"ABC", #"DEF"];
NSArray *b = #[#"123", #"ABC", #"---"];
NSIndexSet *indexes = [a indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
{
return [b containsObject:obj]
}];
NSLog(#"%#", indexes);
NSArray *array1=[[NSArray alloc]initWithObjects:#"ABC",#"DEF", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"ABC",#"123",#"DEF",#"DEF", nil];
for (int i=0; i<array1.count; i++){
for (int j=0; j<array2.count; j++) {
if ([[array1 objectAtIndex:i] isEqualToString: [array2 objectAtIndex:j]]) {
NSLog(#"Matched Indexes %d %#", i, [array1 objectAtIndex:i] );
}
}
}

How to count duplicates values in NSArray?

Value of my NSArray includes the duplicates.
I find the duplicates but now how can I find the no. they repeat?
You can use NSCountedSet for this. Add all your objects to a counted set, then use the countForObject: method to find out how often each object appears.
Example:
NSArray *names = [NSArray arrayWithObjects:#"John", #"Jane", #"John", nil];
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:names];
for (id item in set) {
NSLog(#"Name=%#, Count=%lu", item, (unsigned long)[set countForObject:item]);
}
You can try something like this
__block NSInteger elementCount = 0;
NSArray *array;
[<#NSArray yourArray#> indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop){
if (obj == <#yourObject#>) {
elementCount++;
return YES;
}
return NO;
}];
Let me know if that works for you

Resources