I have two NSMutableArrays, filled with dictionary objects, like
NSMutableArray * bigArray = #[dictionary1,dictionary2,dictionary3];
NSMutableArray * smallArray = #[dictionary1];
I want to remove the common elements (dictionay1) from the big array
Note: small array is subset of big array and elements are distinct
[bigArray removeObjectsInArray:smallArray];
removes all objects in bigArray that are contained in smallArray.
If each element is distinct (as you say in the comments) then you can filter the array using a predicate that removes all the objects that inside of the smaller subset. Since you are using an array in your question, I assume that the order of the objects matter.
If you need to preserve the order, then you will need to filter the array. Converting to a set will break the order (unless you are able to resort it afterwards.
You are using mutable arrays in your question, so I will do the same. Just be aware that the original array is actually modified:
NSMutableArray *original = [NSMutableArray arrayWithArray:#[#"A", #"E", #"C", #"B", #"D"]];
NSMutableArray *subset = [NSMutableArray arrayWithArray:#[#"C", #"B"]];
NSLog(#"before : %#", original);
[original filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
// return YES for objects to keep
return ![subset containsObject:evaluatedObject];
}]];
NSLog(#"after : %#", original);
The output of this code is:
before : (
A,
E,
C,
B,
D
)
after : (
A,
E,
D
)
You can see that the order is preserved.
If you don't want to modify the original array you can produce a new filtered array instead. The small difference is that you call filteredArrayUsingPredicate: instead of filterUsingPredicate:. The predicate is the same:
NSArray *filtered = [original filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
// return YES for objects to keep
return ![subset containsObject:evaluatedObject];
}]];
NSSet is perfect class to do this kind of job. Try that;
NSMutableSet *bigSet = [NSMutableSet setWithArray:bigArray];
[bigSet minusSet:[NSMutableSet setWithArray:smallArray]];
bigArray = [[bigSet allObjects] mutableCopy];
The thing worth to point is that if your array will store duplicate and you convert it to NSSet the duplicate will be removed in that case you shouldn't use it. The second thing is it can change order of the elements in your array.
Related
Consider I have three arrays.
NSArray *array1 = #[#"4",#"3",#"2"];
NSArray *array2 = #[#"2",#"1"];
NSArray *array3 = #[#"3",#"1",#"5",#"2"];
I want to append these arrays. Conditions are:
Order should not change. Means, Array1's order has high priority than Array2.
Duplicate values should be removed (When appending Array2 with Array1, I want to remove duplicate values from Array2)
So I expect the result as like:
#[#"4",#"3",#"2",#"1",#"5"];
Question:
Should I iterate each and every values to construct the expected result? Is there any simple way to achieve it?
Thanks
You can use NSMutableOrderedSet to achieve this:
NSMutableOrderedSet *mSet = [NSMutableOrderedSet new];
[mSet addObjectsFromArray:array1];
[mSet addObjectsFromArray:array2];
[mSet addObjectsFromArray:array3];
NSArray *array = [mSet array];
I have a tableview that i want to search through with a searchable. It worked before but when i added sections i got into trouble because i had to change from arrays to dictionary.
So basically i have a NSDictionary that looks like this
{ #"districtA": array with point objects, #"districtB": array with point objects}
I need to filter them based on the point objects.name that is in the arrays. After that i want to create a new nsdictionary with the filtered objects in it.
I tried at least 10 different methods but i can't figure it out so i think this is the only way that i am most positive that should work.
This is the only way i can think of if there is an easier way or more logic way please tell me.
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name BEGINSWITH[c] %#",searchText];
//create new array to fill
NSArray *arrayWithFilteredPoints = [[NSArray alloc] init];
//loop through the values and put into an rray based on the predicate
arrayWithFilteredPoints = [NSArray arrayWithObject:[[self.PointList allValues] filteredArrayUsingPredicate:predicate]];
NSMutableDictionary *dict = [#{} mutableCopy];
for (Point *point in arrayWithFilteredPoints) {
if (![dict objectForKey:Point.district])
dict[Point.district] = [#[] mutableCopy];
[dict[Point.district]addObject:Point];
}
self.filteredPointList = dict;
self.filteredDistrictSectionNames = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];}
This results in a crash, it happens of course where the predicate is used but i don't know how to debug what predicate i should use:
on 'NSInvalidArgumentException', reason: 'Can't do a substring operation with something that isn't a string (lhs = (
West ) rhs = w)'
I have read the comments and you are right. There was something wrong with my code.
I changed the logic, i added some more steps (like creating NSArray without needing it) to make the solution clear
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name BEGINSWITH[c] %#",searchText];
//1. create new array to fill only the Points from the dictionary
NSArray *allPoints = [self.PointList allValues];
NSMutableArray *allPointObjects = [[NSMutableArray alloc]init];
for (NSArray *array in allPoints) {
for (Point *point in array) {
[allPointObjects addObject:point];
}
}
//2. loop through allPointObjects and put into an mutablearray based on the predicate
NSArray *arrayWithFilteredPoints = [[NSArray alloc] init];
arrayWithFilteredPoints = [allPointObjects filteredArrayUsingPredicate:predicate];
NSMutableDictionary *dict = [#{} mutableCopy];
for (Point *point in arrayWithFilteredPoints) {
if (![dict objectForKey:point.district])
dict[point.district] = [#[] mutableCopy];
[dict[point.district]addObject:Point];
}
self.filteredPointList = dict;
self.filteredDistrictSectionNames = [[dict allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I wanted a filtered nsdictionary at the end that i can pass back to my tableview that reads the dictionary objects based on the keys (districts)
It seems clear from your description that [self.PointList allValues] is not an array of Point objects but an array of arrays of Point objects. That is the source of your difficulty, including your original crash.
You need to decide what to do about that; for example, if you want just one big array of Point objects, then flatten the array of arrays before you filter. I can't advise you further because it is not obvious to me what ultimate outcome you desire.
EDIT You've now modified your code and I can see more clearly what you're trying to do. You have a dictionary whose values are arrays of points, and you are trying to filter some of the points out of each array. What I would have done is to do that - i.e., run thru the keys, extract each array, filter it, and put it back (or delete the key if the array is now empty). But I can see that what you are doing should work, because you have cleverly put the keys into the points to start with, so you can reconstruct the dictionary structure from that.
I have the following scenario:
I have two collection classes (could be NSArray, NSMutableArray, NSSet, NSOrderedSet or whatever would be best suited for this case), which hold unique objects of the same type (unique in the sense that for all objects in the collections for no two elements the isEqual method would return true).
Lets say the first collection instance holds the following objects (1,2,3,4,5) and the second one (2,3,4,6,7). Now I need a method that returns the difference between the two collections, with the extra info what exactly the difference from each collection was.
An example result for the example would be: (1,5) was removed from the first collection and (6,7) added two the second collection.
I know if I use the NSMutableArray with a sorted list and decide which list has more elements than the other, I could use removeObjectsInArray to get a list of the different objects (like described in Compare two arrays with the same value but with a different order or in How to compare and remove common objects( NSDictionaries) from 2 NSMutableArray?), but don't really know which objects was in which collection. I could create a temporary collection and put the result of removeObjectsInArray in that array and compare the other two initial arrays with the temporary array. Seems little verbose though. Is there a better way that I don't know of?
I found a much slicker way for you to do what you want by using NSPredicate. When I run the following code:
NSArray *firstArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7", nil];
NSArray *secondArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"8",nil];
NSArray *itemsMissingFromSecondArray = [firstArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT SELF IN %#", secondArray]];
NSArray *itemsMissingFromFirstArray = [secondArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT SELF IN %#", firstArray]];
NSLog(#"itemsMissingFromFirstArray=%#\nitemsMissingFromSecondArray=%#", itemsMissingFromFirstArray, itemsMissingFromSecondArray);
I get the following output showing what was missing from each array that was in the other array:
itemsMissingFromFirstArray=(
8
)
itemsMissingFromSecondArray=(
4,
5,
6,
7
)
Less code than sorting and merging, doesn't use a bunch of temporary arrays, and simple enough to read.
NOTE: If someone also wants to know the items that are in both arrays, the solution is similarly simple:
NSArray *itemsFoundInBothArrays = [firstArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF IN %#", secondArray]];
I think if you want to know the difference, you can make use of NSMutableSet and the minusSet function.
[set1 minusSet:set2];
will give you the elements in set1 but not in set2 straight away. So you don't need any temp collection and compare with original collection again.
Otherwise, if you only want to remove the elements, you can make use of NSArray and do sth like:
[secondArray removeObjectsInArray:firstArray];
Edited:
To find all the diff in one shot:
[ [set1 unionSet:set2] minusSet: [set1 intersectSet:set2] ];
+ (NSArray *) removeObjectsFromArray :(NSArray *)arrayToRemoveFrom thatAreAlsoIn:(NSArray *)arrayOfItemsToRemove
{
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:arrayToRemoveFrom];
[newArray removeObjectsInArray:arrayOfItemsToRemove];
return newArray;
}
+(void) findArrayDifferences
{
NSArray *bigArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7", nil];
NSArray *smallArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"8",nil];
NSArray *itemsInBigArrayThatAreNotInSmallArray = [self removeObjectsFromArray:bigArray thatAreAlsoIn:smallArray];
NSArray *itemsThatAreInBothArrays = [self removeObjectsFromArray:bigArray thatAreAlsoIn:itemsInBigArrayThatAreNotInSmallArray];
NSArray *itemsInSmallArrayThatAreNotInBigArray = [self removeObjectsFromArray:smallArray thatAreAlsoIn:itemsThatAreInBothArrays];
NSLog(#"itemsInBigArrayThatAreNotInSmallArray=%#\nitemsThatAreInBothArrays=%#\nitemsInSmallArrayThatAreNotInBigArray=%#", itemsInBigArrayThatAreNotInSmallArray, itemsThatAreInBothArrays, itemsInSmallArrayThatAreNotInBigArray);
}
This results in the following output:
itemsInBigArrayThatAreNotInSmallArray=(
4,
5,
6,
7
)
itemsThatAreInBothArrays=(
1,
2,
3
)
itemsInSmallArrayThatAreNotInBigArray=(
8
)
I have a NSArray of NSDictionaries. I need the updated NSArray of NSDictionaries with value of one of the dictionary key updated with new value.
Please see below the NSArray structure. I want to update the value for Key3 inside this structure if Key1 matches with some value (e.g. 2). What would the fastest way of doing this. I do not want to use traditional For loop.
[myArray valueForKeyPath:#"#unionOfObjects.Key3"];
<__NSCFArray 0xe70c10>(
{
Key1 = 1;
Key2 = "New Location";
Key3 = (
Data1,
Data2,
Data3
);
},
{
Key1 = 2;
Key2 = "Old Location";
Key3 = (
Data1,
Data2,
Data4
);
}
)
We can solve it by using predicates:
NSArray *dictionaryArray;
NSNumber *key =#2;
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"Key1=%#",key];
NSArray *result =[dictionaryArray filteredArrayUsingPredicate:predicate];
result array now has all dictionaries having (key1 = 2)
Dictionarys can't be edited directly, they must be NSMutableDictionary to edit. Assuming they are NSMutableDictionary instances:
NSMutableDictionary *dic =result[0];
[dic setObject:someObject forKey:#"key3"];
Let's suppose that you already have an array containing all mutable dictionaries. The first step is to get what dictionaries you need to change:
NSPredicate* predicate= [NSPredicate predicateWithFormat: #"Key1=2"];
NSArray* filteredArray= [myArray filteredArrayUsingPredicate: predicate];
The second step is to replace the Key3 value for each object in the array. If you execute a selector on an array, and NSArray doesn't respond to that selector, the selector is performed on it's objects:
[filteredArray performSelector: #selector(setValue:forKey:) withObject: someValue withObject: #"Key3"];
After this statement you don't need to replace any object in myArray, because the filtered array contains the same objects that are in myArray, which are mutable dictionaries.
I would also go for predicates as #santhu stated on his answer. However, I want to point that when searching an unsorted array linear search (that is, looping through every object and checking if it's the wanted one) is optimal. Probably predicates will provide an speed-up due to their internal routines, but a predicate will still execute the "traditional for loop" you want to avoid and thus the speed-up will not be very significant.
There are other searching algorithms which provide a significant speed-up, but they are only valid for sorted databases (unless you have a quantum iPhone ;) ).
If you are interested on this topic, here you can find some types of search algorithms with an analysis of their complexity.
Let's say I have an NSArray of NSDictionaries that is 10 elements long. I want to create a second NSArray with the values for a single key on each dictionary. The best way I can figure to do this is:
NSMutableArray *nameArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (NSDictionary *p in array) {
[nameArray addObject:[p objectForKey:#"name"]];
}
self.my_new_array = array;
[array release];
[nameArray release];
}
But in theory, I should be able to get away with not using a mutable array and using a counter in conjunction with [nameArray addObjectAtIndex:count], because the new list should be exactly as long as the old list. Please note that I am NOT trying to filter for a subset of the original array, but make a new array with exactly the same number of elements, just with values dredged up from the some arbitrary attribute of each element in the array.
In python one could solve this problem like this:
new_list = [p['name'] for p in old_list]
or if you were a masochist, like this:
new_list = map(lambda p: p['name'], old_list)
Having to be slightly more explicit in objective-c makes me wonder if there is an accepted common way of handling these situations.
In this particular case Cocoa is not outdone in succinctness :)
NSArray *newArray = [array valueForKey:#"name"];
From the NSArray documentation:
valueForKey:
Returns an array containing the
results of invoking valueForKey: using
key on each of the receiver's objects.