How can I get all the possibles combinations of numbers from an array in Objective-C [duplicate] - iphone

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Generating permutations of NSArray elements
Let's say that I have
[1,2]
And I want to get
{1}
{2}
{1, 2}
{2, 3}

I think the name of the thing you're looking for is 'power set'. It sounded fun, so here's a crack at it. I relied on this very concise article for the algorithm. I'm not sure if this is efficient (...actually, I'm sure this is inefficient) over large sets.
// answer the powerset of array: an array of all possible subarrays of the passed array
- (NSArray *)powerSet:(NSArray *)array {
NSInteger length = array.count;
if (length == 0) return [NSArray arrayWithObject:[NSArray array]];
// get an object from the array and the array without that object
id lastObject = [array lastObject];
NSArray *arrayLessOne = [array subarrayWithRange:NSMakeRange(0,length-1)];
// compute the powerset of the array without that object
// recursion makes me happy
NSArray *powerSetLessOne = [self powerSet:arrayLessOne];
// powerset is the union of the powerSetLessOne and powerSetLessOne where
// each element is unioned with the removed element
NSMutableArray *powerset = [NSMutableArray arrayWithArray:powerSetLessOne];
// add the removed object to every element of the recursive power set
for (NSArray *lessOneElement in powerSetLessOne) {
[powerset addObject:[lessOneElement arrayByAddingObject:lastObject]];
}
return [NSArray arrayWithArray:powerset];
}
If you think this is a keeper, you could make it a category method on array and drop the parameter. Test it like this...
NSLog(#"empty = %#", [self powerSet:[NSArray array]]);
NSLog(#"one item = %#", [self powerSet:[NSArray arrayWithObject:#"a"]]);
NSLog(#"two items = %#", [self powerSet:[NSArray arrayWithObjects:#"a", #"b", nil]]);
NSLog(#"three items = %#", [self powerSet:[NSArray arrayWithObjects:#"a", #"b", #"c", nil]]);
I did only these tests, and the output looked good. Spoiler alert: the three item test looks roughly like this on my console (with \n's removed):
three items = ((),
(a),
(b),
(a,b),
(c),
(a,c),
(b,c),
(a,b,c))

Related

Compare array elements with eachother, on duplicates add a certain property together?

I'm trying to loop through my array and find duplicate objects based on the name properties of the objects in the array. Once a duplicate is found I need to combine these objects, the name and unit values stay the same, I just want to add the quantity values together. At this point I want to remove the two duplicates and add the new object to the array instead.
If its two difficult to remove two objects and add another (might mess with the index?) then the new objects could be added to a filtered array, as long as when a duplicate isnt found that is also added to this array. So the new array will contain all of my previous values, with duplicate values combine on quantity.
I have this so far:
NSMutableSet* existingNames = [NSMutableSet set];
NSMutableArray* filteredArray = [NSMutableArray array];
for (id object in ingredientsArray)
{
if (![existingNames containsObject:[object name]])
{
[existingNames addObject:[object name]];
NSLog(#"%#", #"DUPLICATE FOUND");
//create new object to store the new info in.
ingredient *info = [[ingredient alloc] initWithname:[object name] quantity:[/* How do I add the quanitity values of the two objects that are duplicates here? */] unit:[object unit]];
//add this to filtered array.
[filteredArray addObject:object];
//remove object from array.
[ingredientsArray removeObject:object];
}
}
thanks
You cannot modify an array that you are enumerating. The compiler should complain about this, if not, it should crash at runtime.
I think the logic in your code is bit messed up. The if clause checks if the string is not contained in your duplicates array - so "DUPLICATE FOUND" is certainly not true.
It is bad practice to just iterate through id in this case. It would be better if you could type your objects more strongly. It is also advised to adhere to the conventions such as Capitalized class names.
One trick to reduce the iterations is to just iterate through the unique names. There is a trick with NSSet to accomplish this:
NSArray *names = [ingredientsList valueForKeyPath:#"name"];
NSSet *uniqueNames = [NSSet setWithArray:names];
NSArray *resultArray = [NSMutableArray array];
NSPredicate *nameFilter;
for (NSString *ingredientName in uniqueNames) {
predicate = [NSPredicate predicateWithFormat:#"name = %#", ingredientName];
NSArray *entries = [ingredientsList filteredArrayUsingPredicate:predicate];
Ingredient *ingredient = entries[0];
if (entries.count > 1) {
NSLog(#"Found %d instances of %#.", entries.count, ingredientName);
NSNumber *sum = [entries valueForKeyPath:#"#sum.quantity"];
ingredient.quantity = sum;
}
[resultsArray addObject:ingredient];
}
This assumes that the class Ingredient has at least two properties, name (NSString) and quantity (NSNumber). This also works with plain NSDictionaries.

ios how can I compare nsdictionary inside of nsmutablearray

I have to 2 NSMutableArrays containing NSMutableDictionarys. I need to verify the dictionaries in one of the arrays exists in the other array. How can I do this?
I'm not sure if this is the best approach but it is an easy one. I created a method to verify if a NSDictionary is present inside a NSArray. Inside this function, I convert the NSDictionarys to NSStrings and compare them.
- (BOOL)verifyIfDictionary:(NSDictionary *)dict existsInsideArray:(NSArray *)array
{
BOOL result = NO;
NSString *dictStr = [NSString stringWithFormat:#"%#",dict]; // convert dictionary to string
for (NSDictionary *d in arrayOfDictionaries)
{
NSString *dStr = [NSString stringWithFormat:#"%#",dict]; // same conversion as above conversion
// now, I just have to compare the resulting strings
if ([dictStr isEqualToString:dStr])
result = YES;
}
return result;
}
Now you just have to iterate through one of your NSArrays and use this method, like this:
NSArray *arrayOfDictionaries1;
NSArray *arrayOfDictionaries2;
// initialize them, fill with data, do your processing.. etc, etc...
// then, when you want to verify:
for (NSDictionary *dict in arrayOfDictionaries1)
{
if ([self verifyIfDictionary:dict existsInsideArray:arrayOfDictionaries2])
{
NSLog(#"exists!");
}
}
To check if array1 contains all of the items in array2:
NSMutableArray *mutableArrayToCheck = [array2 mutableCopy];
[mutableArrayToCheck removeObjectsInArray:array1];
if (array2.count > 0 && mutableArrayToCheck.count == 0) {
// array2 contains all the items in array1
}
Ignore the type of the contents of the arrays for a second, just think of numbers. We have 2 arrays:
array1 = [ 1, 2, 3 ]
array2 = [ 1, 3 ]
If we remove the items in array1 from array2, and the result is an empty array, then we know that all of the items in array2 were also in array1.
removeObjectsInArray: does this for us by searching through the array and comparing each item. We just need to check the result.
This works no matter what the contents of the array are, so long as they implement hash and isEqual:.

NSMutableArray remove duplicate items

NSLog(#"Ci sono %d categorie",category.count);
for(int c=0; c<category.count; c++){
CategoryArray * element =[category objectAtIndex:c];
NSLog(#"******************************");
NSLog(#"Titolo %#",element.label);
NSLog(#"Source %#",element.url);
NSLog(#"******************************");
[element release];
This is my code! I need to remove all duplicates array... I use this code but I don't understand why it doesn't work.
uniquearray = [[NSSet setWithArray:category] allObjects];
Your code does work. Try this:
NSArray *a = [NSArray arrayWithObjects:#"a", #"b", #"c", #"d", #"b", #"b", #"d", nil];
NSLog(#"a: %#\ndistinctA: %#", a, [[NSSet setWithArray:a] allObjects]);
The output is:
a: (
a,
b,
c,
d,
b,
b,
d
)
distinctA: (
c,
d,
a,
b
)
Knowing that the code works for an array that contains duplicate objects, you now have to wonder why objects that you think are duplicates are not considered to be so. The existing answers regarding hash and isEqual point you in the right direction.
The elements inside this CategoryArray must correctly implement isEqual and hash for the NSSet object to be able to figure out which ones are duplicates.
You can see another SO question about overriding isEqual and hash here: Best practices for overriding isEqual: and hash
Set can not contain any duplicate records. It uses a hash to tell them apart and will use isEqual if the hash is same (see the comment by Rudy Velthuis).
you can use following code.
NSMutableArray* filterResults = [[NSMutableArray alloc] init];
BOOL copy;
if (![category count] == 0) {
for (CategoryArray *a1 in category) {
copy = YES;
for (CategoryArray *a2 in filterResults) {
if ([a1.label isEqualToString:a2.label] && [a1.url isEqualToString:a2.url]) {
copy = NO;
break;
}
}
if (copy) {
[filterResults addObject:a1];
}
}
}
And you will get category array without duplicate values.
you can see Edit code. i think this will work.
If you are worried about the order, then you can do it this way.
NSArray * newArray = [[NSOrderedSet orderedSetWithArray:oldArray] array]; // iOS 5.0 and later

compare two arrays and get the common items

I have two arrays, but they have different lengths. I want to compare these two arrays and put common items to a new array. meanwhile there should not be have duplicate items is the third array.
I really mess up with this, please give me a help.
highly thankful . . .
Something like this?
NSMutableSet* set1 = [NSMutableSet setWithArray:array1];
NSMutableSet* set2 = [NSMutableSet setWithArray:array2];
[set1 intersectSet:set2]; //this will give you only the objects that are in both sets
NSArray* result = [set1 allObjects];
This has the benefit of not looking up the objects in the array, while looping through another array, which has N^2 complexity and may take a while if the arrays are large.
Edit: set2 doesn't have to be mutable, might as well use just
NSSet* set2 = [NSSet setWithArray:array2];
A third approach (besides using sets or the simple loop checking each item with contains) would be to sort both arrays, and then use two indices:
// approach using sets:
NSArray *arrayUsingSets(NSMutableArray *arr1, NSMutableArray *arr2)
{
NSMutableSet *set1 = [NSMutableSet setWithArray: arr1];
NSSet *set2 = [NSSet setWithArray: arr2];
[set1 intersectSet: set2];
return [set1 allObjects];
}
// my approach:
NSArray *arrayUsingComp(NSMutableArray *arr1, NSMutableArray *arr2)
{
NSMutableArray *results = [NSMutableArray arrayWithCapacity: arr1.count + arr2.count];
// Assumes input arrays are sorted. If not, uncomment following two lines.
// [arr1 sortUsingSelector: #selector(compare:)];
// [arr2 sortUsingSelector: #selector(compare:)];
int i = 0;
int j = 0;
while ((i < arr1.count) && (j < arr2.count))
{
switch ([[arr1 objectAtIndex: i] compare: [arr2 objectAtIndex: j]])
{
case NSOrderedSame:
[results addObject: [arr1 objectAtIndex: i]];
i++, j++;
break;
case NSOrderedAscending:
i++;
break;
case NSOrderedDescending:
j++;
break;
}
}
// NOTE: results are sorted too.
// NOTE 2: loop must go "backward".
for (NSInteger k = results.count - 1; k > 0; k--)
if ([[results objectAtIndex: k] isEqual: [results objectAtIndex: k-1]])
[results removeObjectAtIndex: k];
return results;
}
I did some simple profiling, and if I make mutable copies of the arrays passed in, and sort those, it performs 1.5 times slower than the approach using sets. My approach above seems to perform 1.5 times faster than the approach using sets. If the arrays are guaranteed to be sorted already, my approach will perform even better yet (almost 4 times as fast as the version using sets), since no sorting is required.
Update:
This did not eliminate duplicates, so I added the loop at the end of the routine. Now it is only 3 times as fast as the approach using sets, but still...
Iterate over array1 & search for it in array2. If it is found, add it to array3 if it does not have it already.
for (MyObject* obj in array1)
{
if([array2 containsObject:obj] && ![array3 containsObject:obj])
[array3 addObject:obj];
}
If your array1 does not have duplicate items, you don't need the 2nd check.

Restrict Duplicate entry in NSArray

I have an array, which contains some duplicate entries.
Firstly, is there any way to restrict duplicate entries when data getting inserted?
Secondly, if an array already having duplicate values than in some other way, we can retrieve only unique values from that array, I heard about NSSet about this, but I have no idea how to use it.
Don't use an NSSet.
You can only insert elements upon creation and cannot change the elements contained after you have created it.
If you want to add and remove objects on the fly, you can use an NSMutableSet.
Here is a demo of how to use it both NSSet and NSMutableSet, then converting the NSSet back to an NSArray (incase you want to do that):
- (void) NSMutableSetPrintTest
{
NSMutableSet *mutableSet = [[NSMutableSet alloc] init];
NSLog(#"Adding 5 objects (3 are duplicates) to NSMutableSet");
NSString *firstString = #"Hello World";
[mutableSet addObject:firstString];
[mutableSet addObject:#"Hello World"];
[mutableSet addObject:#"Goodbye World"];
[mutableSet addObject:#"Goodbye World"];
[mutableSet addObject:#"Goodbye World"];
NSLog(#"NSMutableSet now contains %d objects:", [mutableSet count]);
int j = 0;
for (NSString *string in mutableSet) {
NSLog(#"%d: %# <%p>", j, string, string);
j++;
}
NSLog(#"Now, if we are done adding and removing things (and only want to check what is in the Set) we should convert to an NSSet for increased performance.");
NSSet *immutableSet = [NSSet setWithSet:mutableSet];
NSLog(#"NSSet now contains %d objects:", [immutableSet count]);
int i = 0;
for (NSString *string in immutableSet) {
NSLog(#"%d: %# <%p>", i, string, string);
i++;
}
[mutableSet release]; mutableSet = nil;
NSLog(#"Now, if we are done with the sets, we can convert them back to an NSArray:");
NSArray *array = [immutableSet allObjects];
NSLog(#"NSArray contains %d objects", [array count]);
int k = 0;
for (NSString *string in array) {
NSLog(#"%d: %# <%p>", k, string, string);
k++;
}
}
NSMutableSet is probably the most logical thing to use.
However, be warned that a set does not maintain order of its elements (since a set, by definition, is unordered).
If that's a problem for you, then you have a couple of options:
duplicate set functionality with an NSMutableArray by invoking containsObject: before every call to addObject: (doable, but potentially slow, since arrays have O(n) search time)
use another object.
If you go with the second option, I would recommend taking a look at the excellent CHDataStructures framework, which has a subclass of NSMutableSet called CHOrderedSet, which is a set that maintains insertion order. (And since it's a subclass, it has the exact same API as an NSMutableSet)
If you've heard about NSSet, did you read the documentation? The API is similar to NSArray and very straightforward. Just like NSArray vs. NSMutableArray, you would use NSMutableSet if you need on the fly membership tests.