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.
Related
I am trying to tackle the following sorting and separating, I have an array with IDs like 1,2,3,4,5,3,2,1.
Sorting that array with NSPredicate is quite straightforward but how can i also separate same IDs in separate sub-arrays like [[1,1][2,2,],[3,3],[4],[5]]? I guess one option is to loop the sorted array and compare previous index ids, but I am wondering if any helper function exist in iOS, I am currently reading about NSOrderedSet but cant seem to find if it can help.
In Swift , with functional programming:
let indexes = [1,2,3,4,5,3,2,1]
let notRepeatedIndexesSet = Set(indexes)
let notRepeatedIndexesArray = Array(notRepeatedIndexesSet).sorted(<)
let yourArray = notRepeatedIndexesArray.map{
number -> [Int] in
Array(count: indexes.filter { $0 == number }.count, repeatedValue:number)
}
A combination of ordered and counted sets:
NSArray *array = #[#1,#2,#3,#4,#5,#3,#2,#1];
NSOrderedSet *os = [[NSOrderedSet alloc] initWithArray:array];
NSCountedSet *cs = [[NSCountedSet alloc] initWithArray:array];
NSMutableArray *sortedArray = [#[] mutableCopy];
[os enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSMutableArray *countArray = [#[] mutableCopy];
for (int i = 0; i < [cs countForObject:obj]; ++i) {
[countArray addObject:obj];
}
[sortedArray addObject:[countArray copy]];
}];
i have 2 nsarrays
1 with nsdictionary's another with nsnumbers
NSArray *arr1 = #[#{#"id":#1},#{#"id":#2},#{#"id":#3},#{#"id":#4}];
NSArray *arr2 = #[#3,#1,#4,#2];
and i want to sort my arr1 through their id following the order of arr2
is this possible?
The problem with using sortedArrayUsingComparator: is you start dealing with O(n^2) lookup times. For each sort comparison in the first array, you have to do a lookup in the second array.
Your best bet is to take advantage of a hash table to reduce that to O(n) average complexity.
Your first step is to create a dictionary using id as a key. The result would look something like #{#1: #{#"id":#"1"}, ...}. Then you just have to construct an array by looping through arr3 and grabbing the values.
NSArray *arr1 = #[#{#"id":#1},#{#"id":#2},#{#"id":#3},#{#"id":#4}];
NSArray *arr2 = #[#3,#1,#4,#2];
NSMutableDictionary *map = [NSMutableDictionary dictionary];
for (NSDictionary *item in arr1) {
map[item[#"id"]] = item;
}
NSMutableArray *arr3 = [NSMutableArray array];
for (id key in arr2) {
[arr3 addObject:map[key]];
}
This solution of course assumes parity between the two arrays. If arr2 has an element not in arr1 it will crash when trying to add nil to arr3. If arr1 has a value not in arr2 it will be excluded from arr3. These are risks you will have to address based on your requirements.
Here is how you can do it by using a custom comparator:
NSArray* sorted= [arr1 sortedArrayUsingComparator: ^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
return [arr2 indexOfObject:obj1[#"id"]] - [arr2 indexOfObject:[obj2[#"id"]];
}];
I exploited the fact that NSComparisonResult has +1 to represent an ascending order, -1 for descending and 0 to represent the same order.
- (NSArray*) sortedArray
{
NSArray *arr1 = #[#{#"id":#1},#{#"id":#2},#{#"id":#3},#{#"id":#4}];
NSArray *arr2 = #[#3,#1,#4,#2];
NSMutableArray *mutableArray = [NSMutableArray new];
for (NSNumber *number in arr2)
{
for (NSDictionary* dictionary in arr1)
{
NSNumber *number2 = dictionary[#"id"];
if ([number isEqual:number2])
{
[mutableArray addObject:dictionary];
break;
}
}
}
return mutableArray;
}
This question already has answers here:
The best way to remove duplicate values from NSMutableArray in Objective-C?
(14 answers)
Closed 9 years ago.
i have a nsmutablearray as is shown below. I would like to retrieve list value without redundancy. in this example i should retrive 10 20 30
NSMutableArray *array = [[NSMutableArray alloc]initWithObjects:#"10",#"20",#"30",#"30",#"30",#"30",#"20", nil];
Transform the array into a set, and then back into an array.
NSSet *set = [NSSet setWithArray:array];
NSArray *a = [set allObjects];
You can also have this new array sorted:
NSArray *a2 = [a sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];
Since iOS 5 you can use -[NSOrderedSet orderedSetWithArray:] which preserves the order:
NSArray *a2 = [[NSOrderedSet orderedSetWithArray:array] array];
NSArray *withRedunecy = [[NSSet setWithArray: array] allObjects];
This will be the one way like you can create new NSArray which has no duplicate objects or you need to create a logic for getting unique objects like insert one by one with checking is it already present or not.
Try to use this on
NSArray *array = #[#"10",#"20",#"30",#"30",#"30",#"30",#"20"];
NSArray *newArray = [[NSSet setWithArray:array] allObjects];
NSLog(#"%#", newArray);
Output :
(
30,
20,
10
)
Only this line of code will work fine .
NSSet *mySet = [NSSet setWithArray:array];
now mySet will have unique elements.so create array with this set
NSArray *myarray = [mySet allObjects];
We have an app that calls a SOAP web service and retrieves a long list of XML, which the app then parses into an NSArray of NSDictionary objects. The NSArray contains a list of Rental Apartment information, each of which is stored into an NSDictionary.
The entire list may contain 10 different types of Apartments (i.e. 2-room, 3-room), and we need to split the NSArray into smaller NSArrays based on Room-Type, which has the key "roomType" in the NSDictionary objects.
Currently our algorithm is
Use [NSArray valueForKeyPath:#"#distinctUnionofObjects.room-type"]
to obtain a list of unique room-type values.
Loop through the list of unique room-type values
For each unique room-type value, use NSPredicate to retrieve matching items from the Original list
Our code is below (renamed for clarity):
NSArray *arrOriginal = ... ...; // Contains the Parsed XML list
NSMutableArray *marrApartmentsByRoomType = [NSMutableArray arrayWithCapacity:10];
NSMutableArray *arrRoomTypes = [arrOriginal valueForKeyPath:#"distinctUnionOfObjects.roomType"];
for(NSString *strRoomType in arrRoomTypes) {
NSPredicate *predicateRoomType = [NSPredicate predicateWithFormat:#"roomType=%#", strRoomType];
NSArray *arrApartmentsThatMatchRoomType = [arrOriginal filteredArrayUsingPredicate:predicateRoomType]; // TAKES A LONG TIME EACH LOOP-ROUND
[marrApartmentsByRoomType addObject:arrApartmentsThatMatchRoomType];
}
However, step 3 is taking a long time as the original list may contain large amount (>100,000) of items. It seems that NSPredicate goes through the entire list for each key value. Is there a more efficient way of splitting a large NSArray into smaller NSArrays, based on NSDictionary keys?
If the order of your splited Arrays is not important, i have a solution for you:
NSArray *arrOriginal;
NSMutableDictionary *grouped = [[NSMutableDictionary alloc] initWithCapacity:arrOriginal.count];
for (NSDictionary *dict in arrOriginal) {
id key = [dict valueForKey:#"roomType"];
NSMutableArray *tmp = [grouped objectForKey:key];
if (tmp == nil) {
tmp = [[NSMutableArray alloc] init];
[grouped setObject:tmp forKey:key];
}
[tmp addObject:dict];
}
NSMutableArray *marrApartmentsByRoomType = [grouped allValues];
This is quite performant
- (NSDictionary *)groupObjectsInArray:(NSArray *)array byKey:(id <NSCopying> (^)(id item))keyForItemBlock
{
NSMutableDictionary *groupedItems = [NSMutableDictionary new];
for (id item in array) {
id <NSCopying> key = keyForItemBlock(item);
NSParameterAssert(key);
NSMutableArray *arrayForKey = groupedItems[key];
if (arrayForKey == nil) {
arrayForKey = [NSMutableArray new];
groupedItems[key] = arrayForKey;
}
[arrayForKey addObject:item];
}
return groupedItems;
}
Improving #Jonathan answer
Converting array to dictionary
Maintaining the same order as it was in original array
//only to a take unique keys. (key order should be maintained)
NSMutableArray *aMutableArray = [[NSMutableArray alloc]init];
NSMutableDictionary *dictFromArray = [NSMutableDictionary dictionary];
for (NSDictionary *eachDict in arrOriginal) {
//Collecting all unique key in order of initial array
NSString *eachKey = [eachDict objectForKey:#"roomType"];
if (![aMutableArray containsObject:eachKey]) {
[aMutableArray addObject:eachKey];
}
NSMutableArray *tmp = [grouped objectForKey:key];
tmp = [dictFromArray objectForKey:eachKey];
if (!tmp) {
tmp = [NSMutableArray array];
[dictFromArray setObject:tmp forKey:eachKey];
}
[tmp addObject:eachDict];
}
//NSLog(#"dictFromArray %#",dictFromArray);
//NSLog(#"Unique Keys :: %#",aMutableArray);
//Converting from dictionary to array again...
self.finalArray = [[NSMutableArray alloc]init];
for (NSString *uniqueKey in aMutableArray) {
NSDictionary *aUniqueKeyDict = #{#"groupKey":uniqueKey,#"featureValues":[dictFromArray objectForKey:uniqueKey]};
[self.finalArray addObject:aUniqueKeyDict];
}
Hope, It will help when client wants final array in same order as input array.
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