Remove all strings with duplicates in an NSArray - objective-c

I am trying to figure out how to implement this in Objective-C.
I want to remove the strings in an NSArray that have appear more than once in the array.
At the end I want to have an array that only has the unique lines in an array (meaning that not just the duplicates are deleted but also the original string that matches the duplicates.)
For example if you had the following array:
NSArray *array = [NSArray arrayWithObjects:#"bob", #"frank", #"sarah", #"sarah", #"fred", #"corey", #"corey", nil];
I would want the new array to look like this:
#"bob", #"frank", #"fred"

Use an NSCountedSet:
NSCountedSet *countedSet = [NSCountedSet setWithArray:yourArray];
NSMutableArray *finalArray = [NSMutableArray arrayWithCapacity:[yourArray count]];
for(id obj in countedSet) {
if([countedSet countForObject:obj] == 1) {
[finalArray addObject:obj];
}
}
#Caleb suggested adding a method to NSCountedSet called -objectsWithCount:,, which I've implemented here:
#interface NSCountedSet (JRCountedSetAdditions)
- (NSArray *) objectsWithCount:(NSUInteger) count;
#end
#implementation NSCountedSet (JRCountedSetAdditions)
- (NSArray *) objectsWithCount:(NSUInteger) count {
NSMutableArray *array = [NSMutableArray array];
for(id obj in self) {
if([self countForObject:obj] == count) {
[array addObject:obj];
}
}
return [array copy];
}
#end
Once that's done, all you need is one line:
NSArray *finalArray = [[NSCountedSet setWithArray:yourArray] objectsWithCount:1];
By the way, this is type-agnostic, so this will work with any Objective-C object. :-)

One liner : uniqueArray = [[NSSet setWithArray:duplicateArray] allObjects]; if you don't care about the ordering :D

A slightly different approach from Jacob's:
NSArray *array = [NSArray arrayWithObjects:#"bob", #"frank", #"sarah", #"sarah", #"fred", #"corey", #"corey", nil];
NSCountedSet *namesSet = [[NSCountedSet alloc] initWithArray:array];
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
[namesSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop){
if ([namesSet countForObject:obj] == 1) {
[namesArray addObject:obj];
}
}];
And
NSLog(#"old: %#\nNew: %#", array, namesArray);
gives:
2011-06-16 18:10:32.783 SetTest[1756:903] old: (
bob,
frank,
sarah,
sarah,
fred,
corey,
corey
)
New: (
frank,
fred,
bob
)
Blocks are your friends! And since NSCountedSet is a subclass of NSSet you can use the block methods that are available there.

Here is the simplest approach to remove duplicate strings:
NSArray *array = [NSArray arrayWithObjects:#"bob", #"frank", #"sarah", #"sarah", #"fred", #"corey", #"corey", nil];
NSArray *distintStrings = [array valueForKeyPath:#"#distinctUnionOfObjects.self"];

Related

NSString to NSArray and editing every object

I have an NSString filled with objects seperated by a comma
NSString *string = #"1,2,3,4";
I need to seperate those numbers and store then into an array while editing them, the result should be
element 0 = 0:1,
element 1 = 1:2,
element 2 = 2:3,
element 3 = 3:4.
How can i add those to my objects in the string ??
Thanks.
P.S : EDIT
I already did that :
NSString *string = #"1,2,3,4";
NSArray *array = [string componentsSeparatedByString:#","];
[array objectAtIndex:0];//1
[array objectAtIndex:1];//2
[array objectAtIndex:2];//3
[array objectAtIndex:3];//4
I need the result to be :
[array objectAtIndex:0];//0:1
[array objectAtIndex:1];//1:2
[array objectAtIndex:2];//2:3
[array objectAtIndex:3];//3:4
In lieu of a built in map function (yey for Swift) you would have to iterate over the array and construct a new array containing the desired strings:
NSString *string = #"1,2,3,4";
NSArray *array = [string componentsSeparatedByString:#","];
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[newArray addObject:[NSString stringWithFormat:#"%lu:%#", (unsigned long)idx, obj]];
}];
The first thing you need to do is separate the string into an array of component parts - NSString has a handy method for that : '-componentsSeparatedByString'. Code should be something like this :
NSArray *components = [string componentsSeparatedByString:#","];
So that gives you 4 NSString objects in your array. You could then iterate through them to make compound objects in your array, though you arent exactly clear how or why you need those. Maybe something like this :
NSMutableArray *resultItems = [NSMutableArray array];
for (NSString *item in components)
{
NSString *newItem = [NSString stringWithFormat:#"%#: ... create your new item", item];
[resultItems addObject:newItem];
}
How about this?
NSString *string = #"1,2,3,4";
NSArray *myOldarray = [string componentsSeparatedByString:#","];
NSMutableArray *myNewArray = [[NSMutableArray alloc] init];
for (int i=0;i<myOldarray.count;i++) {
[myNewArray addObject:[NSString stringWithFormat:#"%#:%d", [myOldarray objectAtIndex:i], ([[myOldarray objectAtIndex:i] intValue]+1)]];
}
// now you have myNewArray what you want.
This is with consideration that in array you want number:number+1

Check that the contents of one NSArray are all in another array

I have one NSArray with names in string objects like this:#[#"john", #"smith", #"alex",
#"louis"], and I have another array that contains lots of names. How can I check that all the objects in the first array are in the second?
NSSet has the functionality that you are looking for.
If we disregard performance issues for a moment, then the following snippet will do what you need in a single line of code:
BOOL isSubset = [[NSSet setWithArray: array1] isSubsetOfSet: [NSSet setWithArray: mainArray]];
Use this code..
NSArray *temp1 = [NSArray arrayWithObjects:#"john",#"smith",#"alex",#"loui,#"Jac", nil];
NSArray *temp2 = [NSArray arrayWithObjects:#"john",#"smith",#"alex",#"loui,#"Rob", nil];
NSMutableSet *telephoneSet = [[NSMutableSet alloc] initWithArray:temp1] ;
NSMutableSet *telephoneSet2 = [[NSMutableSet alloc] initWithArray:temp2];
[telephoneSet intersectSet:telephoneSet2];
NSArray *outPut = [telephoneSet allObjects];
NSLog(#"%#",outPut);
output array contains:
"john","smith","alex","loui
as per your requirement.
Run a loop and use isEqualToStiring to verify whether array1 objects exists in mainArray.
int num_of_matches = 0;
for(NSString *name in mainArray)
{
if(array1 containsObject:name){
num_of_matches++;
}
}
if(num_of_matches == [array1 count]{
// All objects present
}else {
// Matched number is equal of number_of_matches
}
If you just need to check if all objects from array1 are in mainArray, you should just use NSSet
e.g.
BOOL isSubset = [[NSSet setWithArray:array1] isSubsetOfSet:[NSSet setWithArray:mainArray]]
if you need to check which objects are in mainArray, you should take a look at NSMutableSet
NSMutableSet *array1Set = [NSMutableSet setWithArray:array1];
[array1Set intersectSet:[NSSet setWithArray:mainArray]];
//Now array1Set contains only objects which are present in mainArray too
Use NSArray filteredArrayUsingPredicate: method. Its really fast to find out similar types of object in both arrays
NSPredicate *intersectPredicate = [NSPredicate predicateWithFormat:#"SELF IN %#", otherArray];
NSArray *intersectArray = [firstArray filteredArrayUsingPredicate:intersectPredicate];
From above code intersect array gives you same objects which are in other array.
Try this way;
NSArray *mainArray=#[#"A",#"B",#"C",#"D"];
NSArray *myArray=#[#"C",#"x"];
BOOL result=YES;
for(id object in myArray){
if (![mainArray containsObject:object]) {
result=NO;
break;
}
}
NSLog(#"%d",result); //1 means contains, 0 means not contains
You can use the concept of [NSArray containsObject:], where your objects will be from your array1 like you say "john","smith","alex","loui"
NSArray *array1 = [NSArray arrayWithObjects:#"a", #"u", #"b", #"v", #"c", #"f", nil];
NSMutableArray *mainArray = [NSMutableArray arrayWithObjects:#"a", #"u", #"I", #"G", #"O", #"W",#"Z",#"C",#"T", nil];
int j=0;
for(int i=0; i < mainArray.count; i++)
{
if (j < array1.count)
{
for( j=0; j <= i; j++)
{
if([[mainArray objectAtIndex:i] isEqualToString:[array1 objectAtIndex:j]] )
{
NSLog(#"%#",[mainArray objectAtIndex:i]);
}
}
}
}

Sort NSMutableArray with strings that contain numbers?

I have a NSMutableArray and it has the users high scores saved into it. I want to arrange the items numerically (the numbers are stored in NSStrings.)Example:4,2,7,8To2,4,7,8What is the simplest way to do this if the data is stored in NSStrings?
This code will do it:
//creating mutable array
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:#"4", #"2", #"7", #"8", nil];
//sorting
[myArray sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
return [str1 compare:str2 options:(NSNumericSearch)];
}];
//logging
NSLog(#"%#", myArray);
It uses blocks, make sure your target OS supports that (It's 4.0 for iOS and 10.6 for OSX).
This code works. I tried it:
NSMutableArray *unsortedHighScores = [[NSMutableArray alloc] initWithObjects:#"4", #"2", #"7", #"8", nil];
NSMutableArray *intermediaryArray = [[NSMutableArray alloc] init];
for(NSString *score in unsortedHighScores){
NSNumber *scoreInt = [NSNumber numberWithInteger:[score integerValue]];
[intermediaryArray addObject:scoreInt];
}
NSArray *sortedHighScores = [intermediaryArray sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#", sortedHighScores);
The output is this:
2
4
7
8
If you have any questions about the code, just ask in the comments. Hope this helps!
The NSMutableArray method sortUsingSelector: should do it:
[scoreArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)]
should do it.
If the array is of nsdictionaries conaining numeric value for key number
isKeyAscending = isKeyAscending ? NO : YES;
[yourArray sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
NSString *str1 = [obj1 objectForKey:#"number"];
NSString *str2 = [obj2 objectForKey:#"number"];
if(isKeyAscending) { //ascending order
return [str1 compare:str2 options:(NSNumericSearch)];
} else { //descending order
return [str2 compare:str1 options:(NSNumericSearch)];
}
}];
//yourArray is now sorted
The answer from Darshit Shah make it smootly
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc]initWithKey:#"rank" ascending:YES selector:#selector(localizedStandardCompare:)];

How to extract unique objects from multiple arrays

EDIT:
I have two different arrays with some repeated strings and i want to create a new array with the only the unique strings.
For instance, take these two arrays:
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Result should be an array with objects "b", and "d"
// since they are the only two that are not repeated in the other array.
EDIT:
// Your starting arrays
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Create two new arrays that only contain the objects
// which are not in the other array:
NSMutableArray *uniqueElementsInArray1 = [array1 mutableCopy];
[uniqueElementsInArray1 removeObjectsInArray:array2];
NSMutableArray *uniqueElementsInArray2 = [array2 mutableCopy];
[uniqueElementsInArray2 removeObjectsInArray:array1];
// Combine the two arrays.
// Result contains objects #"b" and #"d":
NSArray *result = [uniqueElementsInArray1 arrayByAddingObjectsFromArray:uniqueElementsInArray2];
For this you just declare one another temp NSMutableArray . Retrieve whatever data u have from your original array say objectArray. Check whether the temp array have that or not and put it into the temp array. Just refer following code:
for(NSString *str in objectArray)
{
if(![tempArray containsObject:str])
{
[tempArray addObject:str];
}
}
After this u can continue to use tempArray or put tempArray into objectArray if you want to use objectArray further.I think this should work for you.
You can use NSSet as a filter (think of Venn Diagrams in your head):
NSArray *array1 = #[#1,#2,#3,#4,#2,#3];
NSArray *array2 = #[#3,#4,#5,#6,#4,#6];
NSSet *set1 = [NSSet setWithArray:array1]; // [1,2,3,4]
NSSet *set2 = [NSSet setWithArray:array2]; // [3,4,5,6]
METHOD 1 (my favorite):
NSMutableSet *mSet1 = [set1 mutableCopy];
NSMutableSet *mSet2 = [set2 mutableCopy];
[mSet1 minusSet:set2]; // mSet1 = [1,2]
[mSet2 minusSet:set1]; // mSet2 = [5,6]
[mSet1 unionSet:mSet2]; // mSet1 = [1,2,5,6], only the unique elements.
// Now just put it in an immutable collections with a self-docu name...
NSArray *arrayOfUniqueness = [setOfUniqueElementsOnly allObjects];
METHOD 2 (more explicit test, no need for Venn Diagrams):
NSSet *setOfObjsUniqueTo1 = [set1 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set2 containsObject:obj];
}]; // [1,2]
NSSet *setOfObjsUniqueTo2 = [set2 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set1 containsObject:obj];
}]; // [5,6]
NSMutableSet *oneSetToRuleThemAll = [NSMutableSet setWithSet:setOfObjsUniqueTo1];
// [1,2]
[oneSetToRuleThemAll unionSet:setOfObjsUniqueTo2]; // [1,2,5,6]
// Or as an array:
NSArray *anotherArrayOfUniqueness = [oneSetToRuleThemAll allObjects];
METHOD 3 (eschews NSSet, but I would not seat this code opposite the Queen of England at a formal dinner -- it is inelegant):
NSMutableArray *mArray1 = [NSMutableArray new];
NSMutableArray *mArray2 = [NSMutableArray new];
NSIndexSet *uniqueIndexes1 = [array1 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array2 containsObject:obj];
}]; // [0,1,4] (b/c #1 and #2 are unique to array1)
[uniqueIndexes1 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray1 addObject:array1[idx]];
}]; // #[#1,#2,#2]
NSIndexSet *uniqueIndexes2 = [array2 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array1 containsObject:obj];
}]; // [2,3,5] (b/c #5 and #6 are unique to array2)
[uniqueIndexes2 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray2 addObject:array2[idx]];
}]; // #[#5,#6,#6]
NSArray *unionArray = [array1 arrayByAddingObjectsFromArray:array2];
// #[#1,#2,#2,#5,#6,#6]
NSArray *yetAnotherArrayOfUniqueness = [[NSSet setWithArray:unionArray] allObjects];
// #[#1,#2,#5,#6]
Not the questioner's question, but to get an array with duplicates removed (i.e., where each element is unique), similar magic can be done:
//given...
NSArray *arr1 = #[#"a", #"b", #"c"];
NSArray *arr2 = #[#"b", #"c", #"d"];
//...make a single array to rule them all:
NSArray *temp = [arr1 arrayByAddingObjectsFromArray:arr2];
//[a,b,c,b,c,d]
//Make an NSSet from the two:
NSSet *filterSet = [NSSet setWithArray:temp]; // Set has: a,b,c,d
//Finally, transmogrify that NSSet into an NSArray:
NSArray *arrayOfUniqueness = [filterSet allObjects]; // [a,b,c,d]
As per the Apple Docs (emphasis added):
+setWithArray:
Creates and returns a set containing a uniqued collection of the objects contained in a given array.
UPDATE: And see here for a similar question: Remove all strings with duplicates in an NSArray
use Set as a filter, example:
String[] arr = {"a","a","b"};
Object[] uniqueArr = (Object[])new HashSet<String>(Arrays.asList(arr)).toArray();

How do you change the elements within an NSArray?

I am a bit confused as to how arrays are handled in Objective-C.
If I have an array such as
NSarray *myArray = [[NSArray alloc]
initWithObjects:#"N", #"N", #"N", #"N", #"N",
nil];
how do I change the first occurrence to "Y"?
You need an NSMutableArray ..
NSMutableArray *myArray = [[NSMutableArray alloc]
initWithObjects:#"N", #"N", #"N", #"N", #"N",
nil];
and then
[myArray replaceObjectAtIndex:0 withObject:#"Y"];
You can't, because NSArray is immutable. But if you use NSMutableArray instead, then you can. See replaceObjectAtIndex:withObject::
[myArray replaceObjectAtIndex:0 withObject:#"Y"]
Write a helper method
-(NSArray *)replaceObjectAtIndex:(int)index inArray:(NSArray *)array withObject:(id)object {
NSMutableArray *mutableArray = [array mutableCopy];
mutableArray[index] = object;
return [NSArray arrayWithArray:mutableArray];
}
Now you can test this method with
NSArray *arr = #[#"a", #"b", #"c"];
arr = [self replaceObjectAtIndex:1 inArray:arr withObject:#"d"];
logObject(arr);
This outputs
arr = (
a,
d,
c
)
You can use similar method for NSDictionary
-(NSDictionary *)replaceObjectWithKey:(id)key inDictionary:(NSDictionary *)dict withObject:(id)object {
NSMutableDictionary *mutableDict = [dict mutableCopy];
mutableDict[key] = object;
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
You can test it with
NSDictionary *dict = #{#"name": #"Albert", #"salary": #3500};
dict = [self replaceObjectWithKey:#"salary" inDictionary:dict withObject:#4400];
logObject(dict);
which outputs
dict = {
name = Albert;
salary = 4400;
}
You could even add this as a category and have it easily available.