Restrict Duplicate entry in NSArray - iphone

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.

Related

Delete only one item from array having same multiple values

There is an array in my app having multiple same values in it. I need to delete only one value at a time from array whether it has same more values in it.
Level1 Business,
Level2 Economy,
Level2 Economy,
Level1 Business
How this can be achieved, and main thing is that these values are dynamic these can be more or less also. Please guide for above.
Below is what i tried.
if([arr containsObject:[NSString stringWithFormat:#"%d",ind]]){
[arr removeObject:[NSString stringWithFormat:#"%d",ind]];
}
This thing removes all similar entries, not required. Thanks in advance.
try like this,
NSArray *array = [NSArray arrayWithObjects:#"Level1 Business", #"Level2 Economy", #"Level2 Economy", #"Level1 Business", nil];
NSMutableArray *mainarray=[[NSMutableArray alloc]initWithArray:array];
int n=[mainarray indexOfObject:#"Level2 Economy"];//it gives first occurence of the object in that array
if(n<[mainarray count]) // if the object not exist then it gives garbage value that's why here we have to take some condition
[mainarray removeObjectAtIndex:n];
NSLog(#"%#",mainarray);
O/P:-
(
"Level1 Business",
"Level2 Economy",
"Level1 Business"
)
As you say,
[array removeObject:#"SomeObject"];
removes all instances of where isEqual: returns YES. To remove only the first instance, you can use something like
NSUInteger index = [array indexOfObject:#"SomeObject"];
if(index != NSNotFound) {
[array removeObjectAtIndex:index];
}
Use [arr removeObjectAtIndex:yourIndex ] to remove your object at perticular postion at dynamic
Sample Code :
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
NSUInteger obj = [arr indexOfObject:#"hi"]; //Returns the lowest integer of the specified object
[arr removeObjectAtIndex:obj]; //removes the object from the array
NSLog(#"%#",arr);
In your Case :
if([arr containsObject:[NSString stringWithFormat:#"%d",ind]])
{
NSUInteger obj = [arr indexOfObject:[NSString stringWithFormat:#"%d",ind]]; //Returns the lowest integer of the specified object
[arr removeObjectAtIndex:obj];
}
Here your requirement is like definition of NSSet, which contains unique objects only.
But this will implies only if both the same value objects, are really in referring to same memory location as well.
If this is the case then and then, you can try code mentioned below:
// create set from an array
NSSet *telephoneSet = [NSSet setWithArray: myArray];
// create array from a set
NSMutableArray *array = [NSMutableArray arrayWithArray:[set allObjects]];
I don't know whether it will work for your requirement or not. But for that, it would be required to check the object equality level.
Still it might help you as an less line of code.
NSMutableArray *uniques= [[NSMutableArray alloc] init];
for (NSString *word in duplicateWordsArray){
if (!uniques.contains(word)){
[ uniques addObject:word];
}
}
I wrote this from my phone so it isn't formatted for code, but this will do it for you quickly and you'll have an array (uniquearray) that has unique words. Then you can use that one or set your original array = to unique array
NSArray *input = [NSArray arrayWithObjects:#"Level1 Business", #"Level2 Economy", #"Level2 Economy", #"Level1 Business", nil];
NSMutableArray *output = [[NSMutableArray alloc] init];
[output addObject:[input objectAtIndex:0]];
for(NSString *value in input) {
if(![output containsObject:value])
[output addObject:value];
}

error in array cleaning method

I am attempting to use this array cleaning method, and there seems to be an error. I can't spot it, I know the array goes in with 3116 items, comes out with 3116 (and I know for a fact there are three duplicates.
Please advice, thanks!
-(NSArray*) removeDuplicates:(NSArray*)inputArray{
NSMutableArray *arrayToClean = [NSMutableArray arrayWithArray:inputArray];
for (int i =0; i<[arrayToClean count]; i++) {
for (int j=(i+1); j < [arrayToClean count]; j++) {
if ([[arrayToClean objectAtIndex:i] isEqual:[arrayToClean
objectAtIndex:j]]) {
[arrayToClean removeObjectAtIndex:j];
j--;
}
}
}
NSArray *arrayToReturn = [NSArray arrayWithArray:arrayToClean];
return arrayToReturn;
}
NSSet will make this a lot easier:
-(NSArray *)removeDuplicates:(NSArray *)inputArray {
NSSet *unique = [NSSet setWithArray:inputArray];
return [unique allObjects];
}
Please note that a set has no guaranteed order. If you need the objects in the array to be in a specific order then you should sort the resulting array as needed.
It may also be appropriate to use an NSSet instead of the original array, then you don't need to worry about duplicates at all. But this depends on the other needs of your array.
Hey You can use another alternative for this.You can use the NSSet here for this task.
NSSet declares the programmatic interface for static sets of distinct objects
You can use sets as an alternative to arrays when the order of elements isn’t important and performance in testing whether an object is contained in the set is a consideration—while arrays are ordered, testing for membership is slower than with sets.
You Just need To call below method.
-(NSArray *)removeDuplicates:(NSArray *)inputArray {
NSSet *finalData = [NSSet setWithArray:inputArray];
return [finalData allObjects];
}
If really face any problem in above way of cleaning ducplicates then you can try another Alterantive.
-(NSArray *)removeDuplicates:(NSArray *)inputArray {
NSMutableArray *inputArray1=[NSMutableArray arrayWithArray:inputArray];
NSMutableArray *finalARray=[[NSMutableArray alloc]init];
for (id obj in inputArray1)
{
if (![finalARray containsObject:obj])
{
[finalARray addObject: obj];
}
NSLog(#"new array is %#",finalARray);
}
return finalARray;
}
I hope it may help you ...
Here is a helper function I had in a previous project to do the exact same thing
- (NSMutableArray *)removeDuplicates:(NSMutableArray *)sortedArray{
NSMutableSet* valuesAdded = [NSMutableSet set];
NSMutableArray* filteredArray = [[NSMutableArray alloc] init];
NSString* object;
/* Iterate over the array checking if the value is a member of the set. If its not add it
* to the set and to the returning array. If the value is already a member, skip over it.
*/
for (object in sortedArray){
if (![valuesAdded member:object]){
[valuesAdded addObject:object];
[filteredArray addObject:object];
}
}
return filteredArray;
}

How do I find (not remove) duplicates in an NSDictionary of NSArrays?

The title pretty much says it all, but just to clarify: I have an NSMutableDictonary containing several NSMutableArrays. What I would like to do is find any value that is present in multiple arrays (there will not be any duplicates in a single array) and return that value. Can someone please help? Thanks in advance!
Edit: For clarity's sake I will specify some of my variables:
linesMutableDictionary contains a list of Line objects (which are a custom NSObject subclass of mine)
pointsArray is an array inside each Line object and contains the values I am trying to search through.
Basically I am trying to find out which lines share common points (the purpose of my app is geometry based)
- (NSValue*)checkForDupes:(NSMutableDictionary*)dict {
NSMutableArray *derp = [NSMutableArray array];
for (NSString *key in [dict allKeys]) {
Line *temp = (Line*)[dict objectForKey:key];
for (NSValue *val in [temp pointsArray]) {
if ([derp containsObject:val])
return val;
}
[derp addObjectsFromArray:[temp pointsArray]];
}
return nil;
}
this should work
If by duplicates you mean returning YES to isEqual: you could first make an NSSet of all the elements (NSSet cannot, by definition, have duplicates):
NSMutableSet* allElements = [[NSMutableSet alloc] init];
for (NSArray* array in [dictionary allValues]) {
[allElements addObjectsFromArray:array];
}
Now you loop through the elements and check if they are in multiple arrays
NSMutableSet* allDuplicateElements = [[NSMutableSet alloc] init];
for (NSObject* element in allElements) {
NSUInteger count = 0;
for (NSArray* array in [dictionary allValues]) {
if ([array containsObject:element]) count++;
if (count > 1) {
[allDuplicateElements addObject:element];
break;
}
}
}
Then you have your duplicate elements and don't forget to release allElements and allDuplicateElements.

populate NSMutableArray with data from for loop

I'm trying to execute what seems like simple code related to NSMutableArray and I've hit a wall. Here's my code:
NSMutableArray *newArray = [[[NSMutableArray alloc] initWithCapacity:4] retain];
NSArray *existingSection2 = [[NSArray alloc] initWithObjects:#"S2 Item1",#"S2 Item2",#"S2 Item3",#"S2 Item4",nil];
for (int i=0; i<[existingSection2 count]; i++) {
NSString *val = [[NSString alloc] initWithString:[existingSection2 objectAtIndex:i]];
[newArray addObject:val];
NSLog(#"val %i is %# new array now contains: %#",i,val,[newArray objectAtIndex:i]);
NSLog(#"new array description: ",[newArray description]);
}
NSLog(#"what's in newArray: ",[newArray objectAtIndex:0]);
As I understand it, here's what I'm doing:
1) Allocate a new NSMutableArray called newArray with a capacity of 4
2) Allocate an NSArray called existingSection2 with four NSString values
3) Iterate through each of the four NSStrings in existingSection2
3a) Allocate an NSString called val with the contents of the existingSection2 array at position i
3b) Add val to newArray in the next available position
3c) Log some stuff for debugging
4) Log the final contents of newArray for debugging
This code is in application:didFinishLaunchingWithOptions: but here's what my console window shows when I launch in the Simulator:
2011-06-26 15:50:48.203 ArrayTest[14936:207] val 0 is S2 Item1 new array now contains: S2 Item1
2011-06-26 15:50:48.205 ArrayTest[14936:207] new array description:
2011-06-26 15:50:48.205 ArrayTest[14936:207] val 1 is S2 Item2 new array now contains: S2 Item2
2011-06-26 15:50:48.205 ArrayTest[14936:207] new array description:
2011-06-26 15:50:48.206 ArrayTest[14936:207] val 2 is S2 Item3 new array now contains: S2 Item3
2011-06-26 15:50:48.206 ArrayTest[14936:207] new array description:
2011-06-26 15:50:48.206 ArrayTest[14936:207] val 3 is S2 Item4 new array now contains: S2 Item4
2011-06-26 15:50:48.206 ArrayTest[14936:207] new array description:
2011-06-26 15:50:48.207 ArrayTest[14936:207] what's in newArray:
I don't understand why I'm not filling up newArray with those four new NSString objects! I've been searching SO for similar questions, but the answers all seem to be related to not initializing an NSMutableArray which I believe that I'm doing correctly.
Thanks for any help!
You have a few problems which I'd like to point out, and I hope you can learn something from them. Here we go...
Memory management
When you create an object using alloc, new, or copy, such as [[NSArray alloc] ...] or [[NSString alloc] ...], it is returned with a +1 retained count. This means you "own" the object and are responsible for releasing it later by calling release (which you're not currently doing in your code). If you use, for example, [NSArray arrayWith...], then you don't get a +1 retain count (you get an autoreleased object), and if you want it to stick around you have to call retain.
That said, when you do
NSMutableArray *newArray = [[[NSMutableArray alloc] initWithCapacity:4] retain];
The alloc gives you a +1 retain count, and then you're calling retain a second time! This is unnecessary.
However, since you don't need to use your arrays or strings outside this function, you don't need to retain them at all, so you can just use:
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:4];
and
NSString *val = [NSString stringWithString:[existingSection2 objectAtIndex:i]];
// or even more simply
NSString *val = [existingSection2 objectAtIndex:i];
For more details and best practices, please read the Memory Management Programming Guide.
Loops/iteration
Your method of using [existingSection2 count] and i++ in a for loop works, but isn't the best way to do things. Obj-C has what's called Fast Enumeration, which is a lot simpler and I hope you'll use it:
for (NSString *val in existingSection2) {
//...
}
NSLog and format strings
The NSLog function works (similar to C's printf) by taking a format string (which contains format specifiers like %d for integers and %# for Obj-C objects), and then a list of arguments to put in for the specifiers.
When you use
NSLog(#"new array description: ",[newArray description]);
you will only log the string "new array description: " because you don't have a format specifier corresponding to the description. Instead you should do:
NSLog(#"new array description: %#", [newArray description]);
// or even more simply
NSLog(#"new array description: %#", newArray);
There's also more information available on the String Format Specifiers that you can use with NSLog.
Overall
Here's a rewrite of your code using the techniques I described above.
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:4];
NSArray *existingSection2 = [NSArray arrayWithObjects:#"S2 Item1",#"S2 Item2",#"S2 Item3",#"S2 Item4",nil];
for (NSString *val in existingSection2) {
[newArray addObject:val];
NSLog(#"value is %#",val);
NSLog(#"new array contains: %#", newArray);
}
NSLog(#"what's in newArray: %#",[newArray objectAtIndex:0]);
You need to add an %# in your format string when printing the array description.
Also, you don't need to explicitly call description because it gets called automatically when printing an object with %#, as in:
NSLog(#"New array is: %#", newArray);
you have a logging problem:
NSLog(#"new array description: ",[newArray description]);
should be
NSLog(#"new array description: %#",[newArray description]);
bonus help:
NSMutableArray *newArray = [[NSMutableArray alloc] initWithCapacity:4]; // fix memory leak
NSArray *existingSection2 = [[NSArray alloc] initWithObjects:#"S2 Item1",#"S2 Item2",#"S2 Item3",#"S2 Item4",nil];
for (id section in existingSection2) // fast enumeration
{
NSString *val = [[NSString alloc] initWithString:[existingSection2 objectAtIndex:i]];
[newArray addObject:val];
NSLog(#"val %i is %# new array now contains: %#",i,val,[newArray objectAtIndex:i]);
NSLog(#"new array description: %#",[newArray description]);
[val release]; // fix memory leak
}
NSLog(#"what's in newArray: %#",[newArray objectAtIndex:0]);

Problem retrieving data from array iphone?

I declared a nsmutablearray and assign some object but when retrieving object it gives memory location.Here is the code.
NSMutableArray *arrCondiNum = [[NSMutableArray alloc] init];
[arrCondiNum addObject:#"2"];
[arrCondiNum addObject:#"4"];
[arrCondiNum addObject:#"6"];
[arrCondiNum addObject:#"8"];
for(int i = 0;i<[arrCondiNum count];i++){
NSLog(#"Array number %d",[arrCondiNum objectAtIndex:i]);
}
Output it gives
Array number 20820
Array number 20836
Array number 20852
Array number 20868
You add string in your array, then to display it, you have to use %#:
NSLog(#"Array number %#",[arrCondiNum objectAtIndex:i]);
In your code (%d), you display the address of the object.
%# will display the description of the ObjC object (return of -descriptionWithLocale: or -description)
Note that in case you want an array of numbers, use NSNumber instead:
[array addObject:[NSNumber numberWithInt:1]];
You still have to use the %# format specifier though (as NSNumber is a class-type) or retrieve the integer value using -intValue.
Try [[arrCondiNum objectAtIndex:i] floatValue] instead. NSArrays are keeping objects (pointers to objects to be more precise), not values. So the array will return the string object you created earlier. By sending this object the "floatValue" message, it will return its content represented as float value. You could use intValue as well to receive an integer.
just write like this..
NSLog(#"Array number %#",[arrCondiNum objectAtIndex:i]);
u have to print some text..so