Getting Top 10 Highest Numbers From Array? - objective-c

I am having a bit of a issue. I have an NSMutableDictionary with 10 NSMutableArrays in it. Each array has somewhere between 0-10 numbers which could each be any integer, e.g. 12 or 103.
What I need to do is get the top 10 highest numbers from across each of the arrays. The trouble is, I need to keep a reference of the array it came from in the dictionary (the key) and the index position of the number from the array it came form.

Easiest way, is to sort the array in Descending order, and then grab the first 10 indexes
Or if they are inside dictionaries, iterate the dictionary allValues, grab all the arrays, add all the elements inside a common array, and sort that

It seems as if the data structure you want to end up with is an array of objects, where each object is functionally similar to an "index path" except that it's composed of a string (key) and a value (offset).
Assuming that the actual search for highest numbers isn't in question, then I'd suggest creating one of these objects whenever you find a candidate number so that, once the top ten are found, the objects can be used as back-pointers to the numbers' source locations.

Sounds like some sort of homework :)
So you have this:
NSMutableDictionary* source = [#{
#"1" : #[ #10, #20, #100 … ],
#"2" : #[ #8, #42, #17 … ]
} mutableCopy];
So lets start by creating another arrangement:
NSMutableArray* numbers = [NSMutableArray new];
for (NSArray* array in source.allValues)
{
for (NSNumber* number in array)
{
[numbers addObject: #{ #"number" : number, #"parent" : array }];
}
}
This is what we get:
#[
#{ #"number" : #10, #"parent" : <array> },
#{ #"number" : #20, #"parent" : <array> },
…
]
Now we can sort and find the numbers you wanted.
[numbers sortUsingComparator: ^( id lhs, id rhs ){
return [((NSDictionary*) rhs)[#"number"] compare: ((NSDictionary*) lhs)[#"number"]];
}];
NSArray* topNumbers = [numbers subarrayWithRange: NSMakeRange( 0, 10 )];
Here we are. topNumbers contains the numbers you needed along the source array.
This is quite a naive way to do it. It can be optimized both in CPU time and memory usage by a fair amount. But hey, keep it simple is not a bad thing.
Not addressed: what if the tenth and eleventh numbers are equal? (adressed here: Pick Out Specific Number from Array?) range checks. not tested, not even compiled. ;)

Walk through the arrays creating an object/structure for each element, consisting of the numeric "key" value and the "path" (array indices) to the element. Sort the objects/structures so created. (This is referred to as a "tag sort".)
The other approach, if you only need the top N values (where N << total number of entries) is to create an array of N elements, consisting of the above key and path info. Scan through all the arrays and compare each array element to the smallest key of the N currently stored. If you find an element larger than the smallest stored, replace the smallest stored and sort the N elements to select a new smallest stored.

You have to short your array in descending order using 'C' logic. Here i'm going to give an example according to your condition....
// adding 20 elements in an array, suppose this is your original array (array1).
NSMutableArray *array1 = [[NSMutableArray alloc]init];
for(int i=0;i<20;i++)
{
NSString *str = [NSString stringWithFormat:#"%d",(i*4)];
[array1 addObject:str];
}
//make a copy of your original array
NSMutableArray *array2 = [[NSMutableArray alloc]initWithArray:array1];
// this is the array which will get your sorting list
NSMutableArray *array3 = [[NSMutableArray alloc]init];
//declare an integer for compare as a maximum number and it to 0 initially
int max = 0;
// this is the logic to sort an array
for(int i=0;i<20;i++)
{
for(int j=0;j<[array2 count];j++)
{
int f = [[array2 objectAtIndex:j] intValue];
if(max<f)
{
max = f;
}
}
NSString *str = [[NSNumber numberWithInt:max]stringValue];
//max has a maximum value then add it to array3 and remove from array2
//for a new shorting
[array3 addObject:str];
[array2 removeObject:str];
// set 0 to max again
max = 0;
}
//now after all procedure print the **array3**
// and you will get all the objects in descending order,
//you can take top **10** variables from **array3**
NSLog(#"your sorting array %#", **array3**);
}

Related

What's the fastest way to remove duplicates from an array in Objective-C

Prepping for an interview. I am trying to practice by solving the following problem: Given an input array of NSNumbers where some of the numbers are duplicated, how can you create another array that only has the unique values in the original array.
I see 2 approaches:
Brute-force: Loop through each element in the array, while at a element compare it against the set of numbers in the unique list, if there is a match, don't store it, else add it to the unique list. O(n^2) worst case time?
Hash-table based approach: Have a hash-table of length N. Each element of the has-table is NSSet. Every number is mapped to 0,...N-1 using a hashing function. If it is exists in the NSSet corresponding to the "mapped-index", it is not added to "unique array". if not, it is added to set and unique array.
Is this O(N) complexity?
I looked two ways to implement approach 2
A. NSMutableArray with size of N all initialized to [NSNull null] objects at start.
B. NSMutableDictionary where key = hashed mapping integer
Code for each approach is below.
I am noticing that
i. Running time of 2A (array approach) is half of that of 2B (Mutabledictionary approach) for the input array of length 403 shown below(0.055ms vs .12ms).
ii. Running time of 1 is ~ 5 times worse 0.25ms. If there are not any duplicates, this discrepancy is even worse.
My Qs are:
Is there a better algorithm than 2?
Is there a better implementation of algorithm 2?
Why is dictionary approach slower? How can I answer this for myself using Instruments profiling. I.e how can I know exact time taken by each step using Instruments?
Code
Hashcode function
#define NUM_BUCKETS 127
#define RANDOMIZER 11
#define NUM_ITER 40000
int hashcode(int value)
{
int retVal = (value*RANDOMIZER)%NUM_BUCKETS ;
if(retVal<0)
{
retVal+=NUM_BUCKETS ;
}
return retVal ;
}
1. Brute-Force Approach
NSMutableArray *smooshedArr=[[NSMutableArray alloc] init] ;
double startTime ;
startTime=CFAbsoluteTimeGetCurrent() ;
for(int iter=0;iter<=NUM_ITER;iter++)
{
[smooshedArr removeAllObjects] ;
[smooshedArr addObject:ints[0]] ;
int i,j ;
for(i=1;i<[ints count];i++)
{
for(j=0;j<[smooshedArr count];j++)
{
if([ints[i] intValue] == [smooshedArr[j] intValue])
{
break ;
}
}
if(j==[smooshedArr count])
{
[smooshedArr addObject:ints[i]] ;
}
}
}
NSLog(#"Bruteforce took %.3fms to remove duplicates from array of length %lu",(CFAbsoluteTimeGetCurrent()-startTime)*1000/NUM_ITER,(unsigned long)[ints count]) ;
NSLog(#"Smooshed arary is %#",smooshedArr) ;
2A. Array based hash table
NSMutableArray *hashTable = [[NSMutableArray alloc] init] ;
startTime=CFAbsoluteTimeGetCurrent() ;
for(int iter=0;iter<=NUM_ITER;iter++)
{
[smooshedArr removeAllObjects];
for (NSInteger i = 0; i < NUM_BUCKETS; ++i)
{
[hashTable addObject:[NSNull null]];
}
[smooshedArr addObject:ints[0]] ;
int indexToInsert = hashcode([ints[0] intValue]) ;
hashTable[indexToInsert]=[[NSMutableSet alloc] init] ;
[hashTable[indexToInsert] addObject:ints[0]] ;
int i ;
for(i=1;i<[ints count];i++)
{
//Find hascode of element i
//If the list at index = hashcode in hashCodeArary is empty, then create a NSMutableSet, set toInsert = True
//If not empty, check if the element exists in the set. If yes, setToInsert=False. If no, setToInsert=True
int indexToInsert = hashcode([ints[i] intValue]) ;
BOOL toInsert=false ;
if(hashTable[indexToInsert] == [NSNull null])
{
hashTable[indexToInsert]=[[NSMutableSet alloc] init] ;
toInsert=true ;
}
else
{
if(![hashTable[indexToInsert] containsObject:ints[i]])
toInsert=true ;
}
if(toInsert)
{
[hashTable[indexToInsert] addObject:ints[i]] ;
[smooshedArr addObject:ints[i]] ;
}
}
}
NSLog(#"MutableArray (no cheat) took %.3fms to remove duplicates from array of length %lu",(CFAbsoluteTimeGetCurrent()-startTime)*1000/NUM_ITER,(unsigned long)[ints count]) ;
2B. Dictionary based hash table
NSMutableDictionary *hashDict = [[NSMutableDictionary alloc] init] ;
//NSLog(#"Start of hashcode approach %.6f", CFAbsoluteTimeGetCurrent()) ;
startTime=CFAbsoluteTimeGetCurrent() ;
for(int iter=0;iter<=NUM_ITER;iter++)
{
//if(iter <4) NSLog(#"iter start: %.6f", CFAbsoluteTimeGetCurrent()) ;
//if(iter <4) NSLog(#"init start: %.6f", CFAbsoluteTimeGetCurrent()) ;
[smooshedArr removeAllObjects];
[hashDict removeAllObjects] ;
//if (iter<4) NSLog(#"init end: %.6f", CFAbsoluteTimeGetCurrent()) ;
[smooshedArr addObject:ints[0]] ;
int indexToInsert = hashcode([ints[0] intValue]) ;
hashDict[#(indexToInsert)]=[[NSMutableSet alloc] init] ;
[hashDict[#(indexToInsert)] addObject:ints[0]] ;
int i ;
for(i=1;i<[ints count];i++)
{
//Find hascode of element i
//If the list at index = hashcode in hashCodeArary is empty, then create a NSMutableSet, set toInsert = True
//If not empty, check if the element exists in the set. If yes, setToInsert=False. If no, setToInsert=True
int indexToInsert = hashcode([ints[i] intValue]) ;
BOOL toInsert=false ;
if(hashDict[#(indexToInsert)] == nil)
{
hashDict[#(indexToInsert)]=[[NSMutableSet alloc] init] ;
toInsert=true ;
}
else
{
if(![hashDict[#(indexToInsert)] containsObject:ints[i]])
toInsert=true ;
}
if(toInsert)
{
[hashDict[#(indexToInsert)] addObject:ints[i]] ;
[smooshedArr addObject:ints[i]] ;
}
}
}
NSLog(#"Dictionary approach: %.3fms to remove duplicates from array of length %lu",(CFAbsoluteTimeGetCurrent()-startTime)*1000/NUM_ITER,(unsigned long)[ints count]) ;
Input tested ON, 430 elements with some dups and averaged over 40000 iterations
NSArray *ints = #[#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2727272),#(112),#(3),#(4),#(1),#(612211),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(7272),#(1232),#(3),#(4),#(1),#(60),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2727272),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2727272),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(72),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(13272),#(2),#(3),#(4),#(18),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(972),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(3272),#(2),#(3),#(4),#(1),#(69),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(1272),#(2),#(3),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(2272),#(2),#(3),#(4),#(1),#(6),#(91),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(7272),#(2),#(3),#(4),#(12),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(111),#(27272),#(2),#(321),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(4545411),#(12341),#(34210),#(123),#(1234),#(1111),#(727272),#(11187),#(9086),#(876543),#(74532),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(13272),#(2),#(3),#(4),#(18),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(658),#(45454),#(12934),#(38421),#(1243),#(12345),#(1112),#(72),#(52),#(3),#(498),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(45454),#(1234),#(650),#(45454),#(1234),#(3421),#(123),#(1234),#(111),#(27272),#(2),#(321),#(4),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(65),#(4545411),#(12341),#(34210),#(123),#(1234),#(1111),#(727272),#(11187),#(9086),#(876543),#(74532),#(464642),#(65),#(45454),#(1234),#(3421),#(123),#(1234),#(11111),#(13272),#(2),#(3),#(4),#(18),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(45454),#(464642),#(658),#(45454),#(12934),#(38421),#(1243),#(19992345),#(119875412),#(72),#(52),#(3),#(498),#(1),#(6),#(9),#(2),#(2),#(3),#(21),#(22),#(450454),#(46908764642),#(6753435),#(45498754),#(100234),#(65)] ;
If you are preparing for an interview, I would advise you to use the framework classes that are already implemented. Don't reimplement the wheel. Try to solve the problem from top to bottom. Don't think about details (hash functions), think about the algorithm structure:
In pseudocode:
for number in input {
if number appears for the first time {
add number to output
}
}
The only problem we have is how to implement the number appears for the first time. That's the only point that has some performance implications here.
In Objective-C we can use NSSet which is a class created exactly for this problem.
NSArray *input = #[... array of numbers];
NSMutableSet *foundNumbers = [NSMutableSet set];
NSMutableArray *output = [NSMutableArray array];
for (NSNumber *number in input) {
if (![foundNumbers containsObject:number])) {
[foundNumbers addObject:number];
[output addObject:number];
}
}
NSLog(#"Output: %#", output);
You need only one pass of the input array. The only way you could improve performance is using a different structure than NSSet, however NSSet is already highly optimized and it's unlikely you will find a better option.
If you want to think out of the box and the numbers in your input are limited to a small enough range (e.g. 0...65000), you can create a BOOL array with 65000 items, all initialized to NO and use that as a fast set implementation.
However, that will take a lot of memory and it won't pay off unless the input array is very long.
Definitely don't implement your own hash tables, NSDictionary is already a hash table. What you are doing in your second implementation is just a very obfuscated reimplementation of NSDictionary. Buckets work only when you can keep them as a simple array. Once you add hash function to it, you are losing the performance gain.
Also note that the overall quality of code is very important for interviews. Don't use #define to declare a constant. Keep a good coding style (I would strongly advice to use spaces around operators). Use iterators instead of for(;;) Try to name your variables better than hashDict (name your variables for the data they contain).
Now a little secret, there is also a class NSOrderedSet which combines NSArray and NSSet into one object and can solve your problem even easier:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:ints];
NSLog(#"Output: %#", orderedSet);
Actually using NSOrderedSet is not even necessary – one can get away with just NSSet:
NSSet *set = [NSSet setWithArray:ints];
If you need an array as output, Key-Value Coding is here to help:
NSArray *array = [ints valueForKeyPath:#"#distinctUnionOfObjects.self"];
If you don't want to use extra space(hash), if sequence of numbers in array does not matter but still you don't want to be as slow as brute force then you can sort the array and after that remove duplicates in one pass. Time complexity nlog(n) + n

Filtering a NSMutableArray and returning only numbers

I have a NSMutableArray called myMutableArray inside him, I have the following values:
('R$ 118.98','AE 12.00 er','R$ 456.99')
What I would do, is find a way to filter the information contained within this array, thus making it returns only numeric characters, for example:
('118.98','12.00','456.99')
I have a simple code who get the lines inside an array:
for(int x=0; x<[myMutableArray count]; x++){
myMutableArray[x];//We need to find a way to filter and update this informations to only store numbers.
}
What the code I can put in my code to filter the information inside my array to only storing numbers?
Try:
//create a new mutable array to store the modified values
NSMutableArray *arrFinal = [[NSMutableArray alloc] init];
//fast-enumerate within myMutableArray
for (NSString *strCurrent in myMutableArray) {
NSString *strModified = [strCurrent stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]];
[arrFinal addObject:strModified];
}
NSLog(#"%#",[arrFinal description]);

Check duplicate property values of objects in NSArray

I have an NSArray containing objects with a size property.
How can I check if the NSArray has two objects with the same value for size?
Can I do something like:
int i = 0;
for (id item1 in myArray) {
NSDecimalNumber *size1 = [item1 size];
for (id item2 in myArray) {
NSDecimalNumber *size2 = [item2 size];
if ([size1 isEqual:size2]) {
i ++;
}
}
}
if (i > [myArray count]) {
NSLog(#"Duplicate Sizes Exist");
}
Or is there an easier way?
Try this code:
NSSet *myset = [NSSet setWithArray:[myarray valueForKey:#"size"]];
int duplicatesCount = [myarray count] - [myset count];
size here is the object property.
Use NSCountedSet. then add all your objects to the counted set, and use the countForObject: method to find out how often each object appears in your array.
You can check this link also how-to-find-duplicate-values-in-arrays
Hope it helps you
Probably simplest is to sort the array based on the size field and then step through the sorted list looking for adjacent dupes.
You could also "wrap" each object in one that exports the size as its key and use a set. But that's a lot of extra allocations.
But if you only want to know if dupes exist, and not which ones they are, create an NSNumber for each object's size and insert the NSNumbers in a set. The final size will tell you how many dupes.
NSArray *cleanedArray = [[NSSet setWithArray:yourArraywithDuplicatesObjects ] allObjects];
Use Sets this will remove all duplicates objects.Will return NSArrayNSCountedSet and use countForObject: method to find out how often each object appears how many times.

NSMutableArray cannot remove duplicates

I have duplicates in my array and i want to get rid of them, so i run this loop, however it doesn't work. Any one know why?
The array currently has 3 items, 2 duplicates and 1 unique.
for (int x = 0; x <= [array count]; x++) {
if(x > 0){
if([[array objectAtIndex:x - 1] isEqualToString:[array objectAtIndex:x]]){
[array removeObjectAtIndex:x];
}
}
}
You can't iterate over an object and modify it at the same time. Once you remove an object, the indexes of all the objects change. You can try copying the array first and iterate that and make the modifications in the original array, but you still might have to change some of your logic depending on what you're trying to accomplish.
Your algorithm only ever compares items that are next to each other in the array (the items at positions x and x-1). If the duplicates are in any other positions, they won't be found.
The naïve way to fix this is to do a double loop. Compare each item in the array with every item after it. This will start taking an extremely long time as your array becomes bigger.
The correct way to do this is to let the framework handle the operation. Convert your array to a set (which does not have duplicates by definition) and then back to an array:
NSSet * s = [NSSet setWithArray:array];
NSArray * dedupedArray = [s allObjects];
If you need to preserve the order, you'll have to do this in a slightly roundabout way, although this is still faster than the double-loop:
NSMutableSet * itemsSeen = [NSMutableSet set];
NSMutableArray * dedupedArray = [NSMutableArray array];
for( id item in array ){
if( ![itemsSeen containsObject:item] ){
[itemsSeen addObject:item];
[dedupedArray addObject:item];
}
}
I would suggest simply using NSSet ( or NSMutableSet ). It will automatically ensure you have only one of every object.
BUT - notice it is one of every OBJECT. It can have 2 objects that are different but have the same inner value.
If you want to ensure that there are no duplicates in your array, it would be better to use an NSMutableSet rather than an NSMutableArray.
NSMutableSet maintains the invariant that every object in the set is unique.
For example:
NSMutableSet* set = [NSMutableSet set];
NSString* data = #"Data";
[set addObject:data];
[set addObject:data];
The second call to addObject: will do nothing as data is already in the set.

count NSMutableArray size till or from a specific word?

I have got an array, I know how to count its elements, but I need to count elements until a specific word:
NSMutableArray *whatBondInFrame;
whatBondInFrame=[NSMutableArray arrayWithObjects:#"red",#"red",#"red",#"gray",#"red",#"ran",#"gray",#"gray",nil];
I know [ whatBondInFrame count] but, let's say I want to know how many elements I have till the first gray or from the word "ran".
How would I get that?
This isn't tested but it should work:
int loc = 0;
for (loc; loc < [array count]; loc++) {
NSString *str = [array objectAtIndex:loc];
if ([str isEqualToString:#"ran"])
break;
}
int length = array.count-loc;
this gives you the count from the first element named ran.
If you want to know how many elements there are before (till) the word 'ran' then replace the last line with
int length = loc
The NSArray method:
- (NSUInteger)indexOfObject:(id)anObject
Will return the index of the first occurrence on an object, so you can do:
NSUInteger firstRanIndex = [whatBondInFrame indexOfObject:#"ran"];
There is a companion method:
- (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range
Which restricts the search to a given range of the array. There is no method to find the last occurrence, for that you must loop with the above methods.
In conjunction with the count method you can get the numbers you want.