I'm trying to find the second largest number in an array. Why is this code not working?
NSMutableArray *array1=[[NSMutableArray alloc]initWithObjects:#5,#25,#48,#2,#52,#53, nil];
id temp,larg2;int k=0;
while(k<2)
{
for(int j=0;j<5-k;j++)
{
if( [array1 objectAtIndex:j]>[array1 objectAtIndex:j+1])
{
temp=[array1 objectAtIndex:j];
[array1 replaceObjectAtIndex:j withObject:[array1 objectAtIndex:j+1]];
[array1 replaceObjectAtIndex:j+1 withObject:temp];
if(k==1 && j==3). //this statement is not running??
{ larg2=temp;
NSLog(#"The answer is %#",larg2);
}
}
}
k++;
}
NSLog(#"The value of Second Largest Element is %#",larg2);
}
How do I find the second largest element?
There is no need to sort the array if all you need is the 2nd largest item, and the sort algorithm you're using is very poor (It has O(n^2) performance, which means it will get slower with the square of the number of items, so that with just a few hundred items it will start to take a long time to complete, and with a few thousand items it will seem to hang.)
Thus there's no real point in trying to debug your code. It's "putting lipstick on a pig" as the expression goes.
Instead of sorting the array, do a single pass through the array. Set a variable largest and secondLargest. If the current array entry is bigger than largest, check to see if largest is bigger than secondLargest, and replace secondLargest, then replace largest With the new largest value. That will give you O(n) performance (time to completion goes up linearly with the number of items in the array) which is faster than the fastest sort algorithms, and also a lot simpler to implement.
If you don't care about performance, just use a system sort method, then take the 2nd to last item in the sorted array. The system's sort functions are optimized, and typically have O(n log n) performance, which is quite good for sort algorithms.
First create a NSSet before if you need to exclude duplicates then Sort the array descending, pick the second element.
NSArray * unsortedArray = #[#22,#11,#53,#15,#7,#37,#11,#92,#84,#5];
NSSet *numberSet = [NSSet setWithArray: unsortedArray];
NSArray *sortedNumbers = [[numberSet allObjects] sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"self" ascending:NO] ]];
NSNumber *secondHighest;
if ([sortedNumbers count] > 1){
secondHighest = sortedNumbers[1];
}
NSLog(#"%ld", secondHighest);
Without sorting :
NSInteger max1 = -1, max2 = -1;
for (NSInteger i = 1; i < [unsortedArray count]; ++i) {
if ([unsortedArray[i] integerValue] > max1) {
max2 = max1;
max1 = [unsortedArray[i] integerValue];
} else if ([unsortedArray[i] integerValue] > max2 && [unsortedArra1y[i] integerValue] < max1) {
max2 = [unsortedArray[i] integerValue];
}
}
NSLog(#"%ld %ld",max1, max2);
If you have small set of array then you can use sorting method to sort an array but for the large number of element it will take more time with increase in numbers with very poor in performance O(n^2) and second method is simple and performance O(n).
Try this one
NSArray *arr = [[NSArray alloc]initWithObjects:#20,#12,#24, nil];
NSSet *tempSet = [NSSet setWithArray: arr];
NSArray *arr1 = [[tempSet allObjects] sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"self" ascending:YES] ]];
NSLog(#"%#",[arr1 objectAtIndex:1]);
Related
I want to do kind of a weird dictionary sort. I have non-unique values and keys and get something like this
NSArray *counts = [#"1",#"2",#"2",#"3",#"6",#"10"];
NSArray *names =[#"Jerry",#"Marge",#"Jerry",#"Marge",#"Jen",#"Mark"];
The output that I want is an descending ordered list by counts with unique names. I don't want lower values of the same person in my outputted arrays. The output should be.
sortedNames=[#"Mark",#"Jen",#"Marge",#"Jerry"]
sortedCounts=[#"10",#"6",#"3",#"2"];
I would really appreciate some help on this.
NSMutableArray *userNameArray = [[NSMutableArray alloc] init];
NSMutableArray *countArray = [[NSMutableArray alloc] init];
for (NSDictionary *dict in bigDick) {
NSString *nameString =[dict objectForKey:#"Name"];
NSString *countString =[dict objectForKey:#"Count"];
NSInteger countInt = [countString integerValue];
NSNumber *countNumber =[NSNumber numberWithInt:countInt];
[userNameArray addObject:nameString];
[countArray addObject:countNumber];
}
NSArray *namesAscending =[[userNameArray reverseObjectEnumerator]allObjects];
NSArray *countsAscending=[[countArray reverseObjectEnumerator]allObjects];
// Put the two arrays into a dictionary as keys and values
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:countsAscending forKeys:namesAscending];
// Sort the first array
NSArray *sortedCountArray = [[dictionary allValues] sortedArrayUsingSelector:#selector(compare:)];
// Sort the second array based on the sorted first array
// NSArray *sortedNameArray= [dictionary objectsForKeys:sortedCountArray notFoundMarker:[NSNull null]];
NSMutableArray *nameArray =[[NSMutableArray alloc] init];
for (int i=1; i<sortedCountArray.count; i++) {
NSString *name = [dictionary allKeysForObject:sortedCountArray[i]];
if (sortedCountArray[i]!=sortedCountArray[i-1]) {
[nameArray addObject:name];
}
}
an old method is to manual sort the array with numbers, by searching on every iteraton for the biggest value, and when you find the max value take the name from the other vector at index of the max number and move it in new vector...
max = counts[0];
counter = 0;
for (int i=0;i<counts.count;i++)
{
temp = counts[i];
if (max<temp)
max = temp;
counter = i;
}
[new_names addObject: [names objectAtIndex:counter]];
[new_numbers addObject: max];
[numbers removeObjectAtIndex: counter];
[names removeObjectAtIndex:counter];
Try something like this. It should work if you do it this way.
Important! do not remove elements in for from array that you count for the for length.
Your problem is in your algorithm design, if you step through it a line at a time in the debugger you should see what it does and where it goes wrong.
We're not here to write you code, but let's see if we can go through one step of an algorithm to help you one your way:
Useful fact: If you lookup a key in a dictionary and that key does not exist the return value will be nil.
From this: you can use a dictionary to keep track of the names you have seen paired with the highest score so far. You obtain a name,score pair, lookup the name in the dictionary - if you get nil its a new name with a new high score. If it's not nil its the currently known high score, so you can compare and update.
That's a rough algorithm, let's try it. Before we start rather than using literal strings for keys everywhere let's define some constants. This has the advantage that we won't mistype the strings, the compiler will spot if we mistype the constant names. These can be defined at the file level or within a method:
const NSString *kName = #"Name";
const NSString *kCount = #"Count";
Now to the code, in a method somewhere, we'll need a dictionary:
NSMutableDictionary *highScores = [NSMutableDictionary new]; // a single dictionary rather than your two arrays
Now start your loop as before:
for (NSDictionary *dict in bigDict) // same loop as your code
{
and extract the two values as before:
NSString *nameString = dict[kName]; // same as your code, but using modern syntax
NSInteger countInt = [dict[kCount] integerValue]; // condense two lines of your code into one
Now we can lookup the name in our dictionary:
NSNumber *currentScore = highScores[nameString]; // get current high score for user, if any
If the name exists as a key this will return the current associated value - the score in this case, if there is no matching key this will return nil. We can test for this in a single if:
if (currentScore == nil // not seen user before, no high score
|| currentScore.integerValue < countInt) // seen user, countInt is greater
{
The above condition will evaluate to true if we either need to add the name or update its score. Adding & updating a key/value pair is the same operation, so we just need the line:
highScores[nameString] = #(countInt); // add or update score for user
and a couple of braces to terminate the if and for:
}
}
Let's see what we have:
NSLog(#"Output: %#", highScores);
This outputs:
Output: {
Jen = 6;
Jerry = 2;
Marge = 3;
Mark = 10;
}
Which is a step in the right direction. (Note: the dictionary is not sorted, NSLog just displays the keys in sorted order.)
Make sure you understand why that works, copy the code and test it. Then try to design the next phase of the algorithm.
If you get stuck you can ask a new question showing the algorithm and code you've developed and someone will probably help. If you do this you should include a link to this question so people can see the history (and know you're not trying to get an app written for you through multiple questions!)
HTH
Try this.
sortedArray = [yourArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
After sort your array then remove duplicates using following.
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray: sortedArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
How would I take an array with long list of numbers that contains duplicates, so for instance:
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60"nil];
Just imagine that this is a HUGE list of random numbers. Now I'm sure that I have to use something like NSSet for this, but i'm not sure how to execute this. Also, once we identify the duplicates I'm guessing that I would then add those numbers to an array, and then call
[array count];
Any ideas?
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:array];
int duplicates = 0;
for (id object in set) {
if ([set countForObject:object] > 1) {
duplicates++;
}
}
This will calculate how many elements have a duplicate.
A sidenote, that array contains a bunch of strings, no numbers...
Anyway, if the goal is to get just the count you could use the following single line to get it.
NSUInteger diff = [array count] - [[array valueForKeyPath:#"#distinctUnionOfObjects.self"] count];
This uses KVC (Key-Value Coding) to get all distinct objects (that is ones without a dupe) counts them and gets the difference from the original count.
NSCountedSet is perfect for what you want to do.
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60",nil];
NSCountedSet *countedSet = [NSCountedSet setWithArray:array];
__block NSUInteger totalNumberOfDuplicates = 0;
[countedSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
NSUInteger duplicateCountForObject = [countedSet countForObject:obj];
if (duplicateCountForObject > 1)
totalNumberOfDuplicates += duplicateCountForObject;
NSLog(#"%# appears %ld times", obj, duplicateCountForObject);
}];
NSLog(#"Total number of duplicates is %ld", totalNumberOfDuplicates);
produces:
45 appears 1 times
60 appears 2 times
100 appears 3 times
Total number of duplicates is 5
Use filteredArrayUsingPredicate this use a predicate with your condition and return an array with the objects you need.
NSArray* arr=[[NSArray alloc] initWithObjects:#"10",#"11",#"10",#"2", nil];
NSLog(#"%d",[[arr filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF == '2'"]] count]);
I am fetching data from Core Data storage using an NSFetchRequest and store that data in an array - all works great. As a next step, I want to sort that array using NSSortDescriptors like so:
array = [array sortedArrayUsingDescriptors:[NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"score" ascending:NO],
[NSSortDescriptor sortDescriptorWithKey:#"score" ascending:NO comparator:^NSComparisonResult(id obj1, id obj2) {
if ([[[array objectAtIndex:[obj2 integerValue]] valueForKey:#"lessImportantItems"] containsObject:[array objectAtIndex:[obj1 integerValue]]]) {
return (NSComparisonResult)NSOrderedAscending;
} else {
return (NSComparisonResult)NSOrderedDescending;
}
}],
[NSSortDescriptor sortDescriptorWithKey:#"createdAt" ascending:NO], nil]];
The problem I have is that the NSComparator block in the second NSSortDescriptor isn't called (I tried to NSLog). To give some background to my data structure, here's the relevant Core Data object graph section:
What the application does is it compares items with one another. As a first step, the winner in a paired comparison gets a score increment. But also I mark a paired priority, i.e. I add a one-to-many lessImportantItems relationship from the winner to the loser. So, in my array I first try to sort by score, and then, when scores are equal, I also try and sort by paired priority.
Maybe it's because I use score as comparator key twice in a row? But, on the other hand, NSComparator does not allow a relationship to be passed as a key either.
I can't seem to crack this one. Anyone has any ideas? Or perhaps I should take a different approach to sorting?
The second sort descriptor does not make any sense to me. It applies the given comparator
to the score attribute of the objects to compare. So inside the comparator,
obj1, obj2 are the score values of the to-be-compared objects.
It seems that you try to get the underlying objects with
[array objectAtIndex:[obj1 integerValue]]
[array objectAtIndex:[obj2 integerValue]]
but that cannot work. So the second sort descriptor should look like
[NSSortDescriptor sortDescriptorWithKey:#"self" ascending:NO
comparator:^NSComparisonResult(Item *item1, Item *item2) {
// compare item1, item2 ...
}];
But then the next problem arises: How to compare two objects according to priority?
Your code does essentially the following:
if ([item2 valueForKey:#"lessImportantItems"] containsObject:item1]) {
return NSOrderedAscending;
} else {
return NSOrderedDescending;
}
But that is not a proper comparator:
It does not return NSOrderedSame if the objects are equal (not "reflexive"),
For two "unrelated objects" it will return NSOrderedDescending regardless of the
order (not "asymmetric"),
it does not detect if item1 is only indirectly related to item2 (not
"transitive").
But how to sort "unrelated objects"? There is no unique solution. If both B and C are
less important than A, then both A, B, C and A, C, B are valid solutions.
What should the comparator return when comparing B and C?
So I think that cannot be achieved with a sort descriptor and
you have to choose some other algorithm, e.g. "Topological sorting".
If anyone's interested, here's how I achieved the sorting I was after.
I used only the first NSSortDescriptor from the example above to get the array sorted by score, and then I called a further sorting method on that array:
array = [array sortedArrayUsingDescriptors:[NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"score" ascending:NO], nil];
array = [self applyMoreImportantPairOrdering:array];
And here's the method:
+ (NSArray *)applyMoreImportantPairOrdering:(NSArray *)array {
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([[obj valueForKey:#"score"] integerValue] > 0) {
NSMutableSet *lessImportantItemsSet = [NSMutableSet setWithSet:[obj valueForKey:#"lessImportantItems"]];
for (int i = idx - 1; i >= 0; i--) {
NSManagedObject *objAbove = [array objectAtIndex:i];
if ([[obj valueForKey:#"score"] integerValue] == [[objAbove valueForKey:#"score"] integerValue]) {
if ([lessImportantItemsSet containsObject:objAbove]) {
NSUInteger idxAbove = [mutableArray indexOfObject:objAbove];
[mutableArray removeObject:obj];
[mutableArray insertObject:obj atIndex:idxAbove];
}
}
}
}
}];
return [NSArray arrayWithArray:mutableArray];
}
The reason I need the lessImportantItemsSet is because when I move (delete and insert) an item in an array, it loses its lessImportantItems relationships. This I way I maintain the list/set of less important items while I'm done with the particular item.
The goal is to compare two arrays as and check if they contain the same objects (as fast as possible - there are lots of objects in the arrays). The arrays cannot be checked with isEqual: as they are differently sorted.
I already tried the solution posted here (https://stackoverflow.com/a/1138417 - see last code snippet of the post by Peter Hosey). But this doesn't work with differently sorted arrays.
The code I'm using now is the following:
+ (BOOL)arraysContainSameObjects:(NSArray *)array1 andOtherArray:(NSArray *)array2 {
// quit if array count is different
if ([array1 count] != [array2 count]) return NO;
BOOL bothArraysContainTheSameObjects = YES;
for (id objectInArray1 in array1) {
BOOL objectFoundInArray2 = NO;
for (id objectInArray2 in array2) {
if ([objectInArray1 isEqual:objectInArray2]) {
objectFoundInArray2 = YES;
break;
}
}
if (!objectFoundInArray2) {
bothArraysContainTheSameObjects = NO;
break;
}
}
return bothArraysContainTheSameObjects;
}
This works, but those are two nested fast enumerations. Is there a way to do a faster comparison?
As per your code, you are strict to same number of elements and each object of first array should be there in second array and vice versa.
The fastest way would be to sort both the array and compare them.
Ex:
NSArray *array1=#[#"a",#"b",#"c"];
NSArray *array2=#[#"c",#"b",#"a"];
array1=[array1 sortedArrayUsingSelector:#selector(compare:)];
array2=[array2 sortedArrayUsingSelector:#selector(compare:)];
if ([array1 isEqualToArray:array2]) {
NSLog(#"both have same elements");
}
else{
NSLog(#"both having different elements");
}
How about converting both arrays to sets and comparing them.
NSSet *set1 = [NSSet setWithArray:arr1];
NSSet *set2 = [NSSet setWithArray:arr2];
Compare the two using
if([set1 isEqualToSet:set2]) {
}
Use containsObject: method instead of iterating the whole array.
NSArray *array;
array = [NSArray arrayWithObjects: #"Nicola", #"Margherita", #"Luciano", #"Silvia", nil];
if ([array containsObject: #"Nicola"]) // YES
{
// Do something
}
like this
+ (BOOL)arraysContainSameObjects:(NSArray *)array1 andOtherArray:(NSArray *)array2 {
// quit if array count is different
if ([array1 count] != [array2 count]) return NO;
BOOL bothArraysContainTheSameObjects = YES;
for (id objectInArray1 in array1) {
if (![array2 containsObject:objectInArray1])
{
bothArraysContainTheSameObjects = NO;
break;
}
}
return bothArraysContainTheSameObjects;
}
Tried to get the accepted answer working but it wasn't quite the best fit for my situation.
I found this answer and all credit goes to #joel kravets for the method.
Basically sorting using a comparator enables you to sort using objects more easily - hence the problem I was facing when trying to use the above solution.
NSArray * array1 = [NSArray arrayWithArray:users];
NSArray * array2 = [NSArray arrayWithArray:threadUsers];
id mySort = ^(BUser * user1, BUser * user2){
return [user1.name compare:user2.name];
};
array1 = [array1 sortedArrayUsingComparator:mySort];
array2 = [array2 sortedArrayUsingComparator:mySort];
if ([array1 isEqualToArray:array2]) {
NSLog(#"both are same");
}
else{
NSLog(#"both are different");
}
Previously I had tried to use other answers like those above, using break to go through loops but in the end this answer came out easiest probably due to its speed and also that in the end we have the if statement allowing us to put code depending on if they are the same or different.
Thanks to Anoop for getting me on the right track and Joel for helping me to tighten the efficiency of it
If you want to check whether both arrays contain the same duplicates, just use NSCountedSet. It's like an NSSet, but each object in the set also has a count telling you how often it has been added. So
BOOL same = (array1.count == array2.count);
if (same && array.count > 0)
{
NSCountedSet* set1 = [[NSCountedSet alloc] initWithArray:array1];
NSCountedSet* set2 = [[NSCountedSet alloc] initWithArray:array2];
same = ([set1 isEqual: set2]);
}
No matter how you do it, this will be time consuming, so you might consider if there are special cases that can be handled quicker. Are these arrays usually the same, or almost the same, or is it true 99% of the time that they are different and that 99% of the time a random element of array1 is not in array2? Are the arrays often sorted? In that case, you could check whether there are identical objects in identical positions, and then consider only those objects that are not the same. If one array contains objects a, b, c, d, e and the other contains a, b, x, d, y, then you only need to compare the array [c, e] vs. [x, y].
[docTypes containsObject:#"Object"];
It will works for your req. As early as fast it will return boolean value for it.
This way the complexity is O(N^2), if you follow this approach you can't do it with a lower complexity. While instead you can do it with O(N log(N)) if you sort both arrays and then compare them. This way after having them sorted you will do it using isEqualToArray: in other N operations.
NSArray *filtered = [someArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"someParamter == %#", paramValue]]];
if (filtered.count) {
}
the main plus is you can use it for any kind of objects: custom, system, NSDictionary. for example I need to know is my UINavigationController's stack contains MySearchResultsVC and MyTopMenuItemsVC or not:
NSArray *filtered = [self.navigationController.viewControllers filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"class IN %#",
[NSArray arrayWithObjects:
[MySearchResultsVC class],
[MyTopMenuItemsVC class],
nil]]];
if (filtered) {
/* ok, now we can handle it! */
}
I know it's late but i just wanna share what i did..
NSString *stringArr1 = [NSString stringWithFormat:#"%#", array1];
NSString *stringArr2 = [NSString stringWithFormat:#"%#", array2];
if ([stringArr1 isEqual: stringArr2])
NSLog(#"identical");
else
NSLog(#"not");
this is just like comparing "#[#1,#2,#3,#4]" == "[#3,#2,#1,#4]".. which is obviously false..
i guess this will do:
[array1 isEqualToArray:array2];
returns bool;
I have an array of arrays. The contained array's first elements are all NSDate objects. I would like to sort the array containing the arrays in order from most recent to least. For some reason, the below sorting algorithm results in an infinite loop. Can anyone help me out? Thank you.
Best...SL
//array is the array containing all of the other arrays(that have NSDates as their first elements)
//temp is the new array being added to the end of the array, to later be sorted into the correct position.
[array addObject:temp];
NSMutableArray *tempArray;
for (int i=0; i<[array count]; i++)
{
NSDate *session1, *session2;
session1 = [[array objectAtIndex:i] objectAtIndex:0];
session2 = [[array objectAtIndex:[array count]-1] objectAtIndex:0];
if([session1 compare:session2] == NSOrderedDescending)
{
tempArray = [array objectAtIndex:i];
[array insertObject:[array objectAtIndex:[array count]-1] atIndex:i];
[array insertObject:tempArray atIndex:[array count]-1];
}
}
This results in an infinite loop because, in every step, you're inserting two more values into the array. Thus your array is growing faster than you are traversing it. I'm assuming you meant to swap the values.
In any case, a much simpler and more efficient sort is to use the built-in sorting capabilities:
// NSArray *sortedArray, with the unsorted 'array' pulled from some other instance
sortedArray = [array sortedArrayUsingComparator:^(id a, id b) {
return [[b objectAtIndex:0] compare:[a objectAtIndex:0]];
}];
If array is mutable and you want to sort it in place:
[array sortUsingComparator:^(id a, id b) {
return [b[0] compare:a[0]];
}];
If array is immutable or you want to leave it alone and make a sorted copy:
NSArray *sortedArray = [array sortedArrayUsingComparator:^(id a, id b) {
return [b[0] compare:a[0]];
}];