Related
I am trying to sort some list of strings. And names of elements could be almost anything, from real names, strings, dates,... and numbers.
I found NSStringCompareOptions with NSNumericSearch, wich work fast enough and it work nice so :
[1,2,21,3,55,6] --> [1,2,3,6,21,55]
But my problems are negative numbers
[1,2,3,-1,-4,-5] --> [-1,-4,-5,1,2,3]
What it is not right.
I know that Apple stays :
Numeric comparison only applies to the numerals in the string,
not other characters that would have meaning in a numeric representation
such as a negative sign, a comma, or a decimal point.
But my question is how to achieve this, because I know I am not only who do this.
EDIT :
Thanks to Narendra Pandey, but my real case is a little bit complicated, so his answer can't be used here.
So let say I have some dictionary with numbers as keys and strings as values :
dic = {#1:#"123", #2:#"-123", #5:"MyName",...};
then I have array of object with ids.
array = #[{object with id 5}, {object with id 2},...];
and I need sorted array of object by name of properties.
NSStringCompareOptions comparisonOption = NSCaseInsensitiveSearch | NSNumericSearch;
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString * name1 = [dic objectForKey:obj1.someId];
NSString * name2 = [dic objectForKey:obj2.someId];
return [name1 compare:name2 options:comparisonOption];;
}];
EDIT 2:
Maybe I should state that I have solution, but it is 4 times slower that sorting with NSStringCompareOptions
// CHECK IF IT IS NUMBER
NSNumber * number1 = [numberFormatter numberFromString:string1];
NSNumber * number2 = [numberFormatter numberFromString:string2];
//
// NSLog(#"NUMBERS : %#, %#", number1, number2);
if (number1 && number2) {
return [number1 compare:number2];
}
return [string1 compare:string2 options:comparisonOption];
NSArray * numbers = #[#1, #2, #3, #-4, #-5, #6, #9];
NSPredicate * predicateForPositive = [NSPredicate predicateWithFormat:#"integerValue >= 0"];
NSArray * positiveNumbers = [numbers filteredArrayUsingPredicate:predicateForPositive];
NSPredicate * predicateForNegative = [NSPredicate predicateWithFormat:#"integerValue <= 0"];
NSArray * NegativeNumber = [numbers filteredArrayUsingPredicate:predicateForNegative];
NSLog(#"Negative number %#",NegativeNumber);
NSLog(#"Positive number %#",positiveNumbers);
Output:
Negative number (
"-4",
"-5"
)
Positive number (
1,
2,
3,
6,
9
)
Now sort both and concate both Array.
Thanks to Narendra Pandey I found solution for me.
Let me state first that Narendra Pandey solution works, but it is slower, even 3 times.
(in my case 0.014s with NSStringCompareOptions, and 0,042 Narendra Pandey solution).
But if I use his idea and change it a little bit :
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString * name1 = somehow get string1;
NSString * name2 = somehow get string2;
if ([string1 integerValue] <0 && [string2 integerValue]<0) {
return - [string1 compare:string2 options:comparisonOption];
}
return [string1 compare:string2 options:comparisonOption];
}];
this method is faster, in my case 0,015s which is comparable with NSStringCompareOptions.
In this way you avoid to go through whole array at beginning to separate negative and positive numbers, and then sort them.
This function get any array and convert it to sorted Double array
var numberArray: [Any] = [4, 3.9, -23,3, 7.6, -51, 75.3, "0", "-(22)"]
/// This function get any array and convert it to sorted _Double_ array
/// - Note: Non-numerical elements are automatically removed from the array
/// - Parameters:
/// - numbers: array of anything
/// - Returns: sorted _Double_ array
func sortAnyNumbers(_ numbers: [Any]) -> [Double] {
var _numbers: [Double] = []
numbers.forEach { number in
// Delete non-numeric characters
if let numb = Double("\(number)".components(separatedBy: CharacterSet(charactersIn: "-01234567890.").inverted).joined()) {
_numbers.append(numb)
}
}
return _numbers.sorted()
}
print(sortAnyNumbers(numberArray)) //[-51.0, -23.0, -22.0, 0.0, 3.0, 3.9, 4.0, 7.6, 75.3]
I have an NSArray(mainArray) that contains 28 items and I want to divide that array into 7 arrays containing 4 items each. Is it possible for me to grab the first 4 items from mainArray, add them to a subArray, then grab the NEXT 4 items and add them to the subArray, and so on until I iterate through all 28 items? If it is possible, how?
Here's my mainArray:
{item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18, item19, item20, item21, item22, item23, item24, item25, item26, item27, item28}
I need to change it to this (groupedArray):
{item1, item2, item3, item4}, {item5, item6, item7, item8}, {item9, item10, item11, item12}, {item13, item14, item15, item16}, {item17, item18, item19, item20}, {item21, item22, item23, item24}, {item25, item26, item27, item28}
This is what I've tried:
int i; int j;
for (i=0, j=1; i<28; i++, j*=4 ) {
NSArray *smallArray = [mainArray subarrayWithRange:NSMakeRange(0, j)];
[groupedArray addObjectsFromArray:smallArray];
}
Thanks,
DB
Some of your indexes are off. You want something like this:
NSArray *mainArray = ... // the full array
NSMutableArray *groupedArray = ... // the new array
for (NSUInteger i = 0; i < mainArray.count; i += 4) {
NSArray *smallArray = [mainArray subarrayWithRange:NSMakeRange(i, 4)];
[groupedArray addObjectsFromArray:smallArray];
}
Please note that this code will crash if the number of elements in mainArray isn't a multiple of 4.
The code below is SUPPOSED to do this: Wherever current athlete is, when you tap a next button the code below is executed. It should change current athlete to the next athlete in the array, then when it gets to the last person, it needs to circle back to 0 (the first object). and it does do that perfectly. when I reach the end of the array though, it will go to the first person again, which is correct, but from then on will skip the last object in the array. any idea why?
-(void)nextAthlete{
NSUInteger index = [_athleteArray indexOfObject:_currentAthlete];
Athlete *count = _athleteArray[index];
NSLog(#"Current Athlete:%# Index: %lu",count,(unsigned long)index);
if(index < ((_athleteArray.count)-1)){
index++;
_currentAthlete = _athleteArray[index];
_evalSelected = [self getMostRecentEval];
[self updateInformation];
NSLog(#"Index after being smaller than the array count: %lu",(unsigned long)index);
}
if(index == ((_athleteArray.count)-1)){
_currentAthlete = _athleteArray[index];
_evalSelected = [self getMostRecentEval];
[self updateInformation];
index=0;
_currentAthlete = _athleteArray[index];
NSLog(#"Index after being equal to array count: %lu",(unsigned long)index);
}
if(index > ((_athleteArray.count)-1)){
index=0;
_currentAthlete = _athleteArray[index];
}
self.title = [NSString stringWithFormat:#"%#'s Evaluation",_evalSelected.whosEval.full];
}
You don't need all those if statements. You're wrapping nextIndex around with the modulo operator so you don't need to apply all this other logic - setting index to nextIndex should suffice. Also, since they aren't if..else statements, you're incrementing and wrapping the index multiple times in the case of the second to last item in the array. When the second if block executes, so will the third, and so _currentAthlete will skip the last item in this situation.
Anyways, no need to repeat all that logic - make your code something like this:
NSUInteger nextIndex = (index + 1) % _athleteArray.count;
Athlete *count = _athleteArray[index];
NSLog(#"Current Athlete:%# Index: %lu",count,(unsigned long)index);
index=nextIndex;
_currentAthlete = _athleteArray[index];
_evalSelected = [self getMostRecentEval];
[self updateInformation];
self.title = [NSString stringWithFormat:#"%#'s Evaluation",_evalSelected.whosEval.full];
This is kind of weird, though, because you skip the first value of _selectedAthlete. But I suppose you come back to it on the last iteration through the loop.
From your comment "if I begin with athlete at position 3 out of 5, it needs to go to 4, then 5, then 0" it sounds like you wish to advance to the next index treating the array as circular. To do this you can use the modulus operator, %, which for positive numbers is the remainder:
NSUInteger index = [_athleteArray indexOfObject:_currentAthlete];
NSUInteger nextIndex = (index + 1) % _athleteArray.count;
In your case you have 4 athletes so index will be in the range 0 -> 3.
Therefore (index + 1) is in the range 1 -> 4.
Taking the remainder after dividing by 4 gives 1, 2, 3, 0 respectively.
So nextIndex is the "following" index in a circular manner.
if(index < _athleteArray.count)
needs to be
if(index < _athleteArray.count - 1).
Think about it -- if you have 4 athletes in your array, then when index = 3, you will enter the if clause and increment index to 4.
When you execute
_currentAthlete = _athleteArray[index] with index = 4, you get the error.
I have a NSMutableArray oldArray. Now, at one point, this NSMutableArray object gets updated with another NSMutableArray, which may have more, less or same number of elements as the previous NSMutableArray.
I want to compare the old and the new array for changes. What I want are two NSArrays addedArray and removedArray which will contain the indices of the elements which have been added and/or removed from the old array.
This whole problem will be more clear with an example :
oldArray = {#"a",#"b",#"d",#"e",#"g"};
newArray = {#"a",#"c",#"d",#"e",#"f",#"h"};
So, here the removed objects are #"b" and #"g" at indices 1 and 4 respectively. And the objects added are #"c", #"f" and #"h" at indices 1, 4 and 5 (first objects are removed, then added).
Therefore,
removedArray = {1,4}; and addedArray = {1,4,5};
I want an efficient way to get these two arrays - removedArray and addedArray from the old and new NSMutableArray. Thanks! If the problem is not very understandable, I'm willing to provide more information.
Edit 1
Perhaps it will be more clear if I explain what I want to use this for.
Actually what I am using this for is updating a UITableView with methods insertRowsAtIndexPaths and removeRowsAtIndexPaths with animation after the tableview gets loaded, so that the user can see the removed rows go out and the new rows come in. The tableview stores the Favourites elements which the user can add or remove. So after adding some favorites and removing some; when the user comes back to the favourites tableview, the animations will be shown.
Edit 2
Should have mentioned this earlier, but the elements in both the old and the new array will be in an ascending order. Only the indices of the removal or addition matters. The order cannot be changed. ex. {#"b",#"a",#"c",#"d"} cannot be an array.
I have tried iterating through the old and the new arrays using loops and if conditions, but is it getting really messy and buggy.
This is not a simple problem. First, note that it may have multiple solutions:
a b c d
b c d e
both (a={0, 1, 2, 3}, r={0, 1, 2, 3}) and (a={3}, r={0}) are valid solutions. What you are probably looking for is a minimal solution.
One way to get a minimal solution is by finding the Longest Common Subsequence (LCS) of the two sequences. The algorithm for finding LCS will tell you which elements of the two sequences belong to the LCS, and which do not. Indexes of each element of the original array that is not in LCS go into the removed array; indexes of elements of the new array that are not in LCS go into the added array.
Here are a few examples (I parenthesized the elements of LCS):
0 1 2 3 4 5
(a) b (d) (e) g
(a) c (d) (e) f h
The items of old not in LCS are 1 and 4; the items of new not in LCS are 1, 4, and 5.
Here is another example:
0 1 2 3
a (b) (c) (d)
(b) (c) (d) e
Now added is 3 and removed is 0.
addedArray = newArray ∖ (newArray ∩ oldArray)
= newArray ∖ ({#"a",#"c",#"d",#"e",#"f",#"h"} ∩ {#"a",#"b",#"d",#"e",#"g"})
= newArray ∖ {#"a",#"d",#"e"}
= {#"a",#"c",#"d",#"e",#"f",#"h"} ∖ {#"a",#"d",#"e"}
= {#"c",#"f",#"h"}
removedArray = oldArray ∖ (oldArray ∩ newArray)
= oldArray ∖ ({#"a",#"b",#"d",#"e",#"g"} ∩ {#"a",#"c",#"d",#"e",#"f",#"h"})
= oldArray ∖ {#"a",#"d",#"e"}
= {#"a",#"b",#"d",#"e",#"g"} ∖ {#"a",#"d",#"e"}
= {#"b",#"g"}
To find intersections of array, you can view the following SO post:
Finding Intersection of NSMutableArrays
If both arrays are already sorted in ascending order, you can find the added and removed
elements with a single loop over both arrays (using two independent pointers into the array):
NSArray *oldArray = #[#"a",#"b",#"d",#"e",#"g"];
NSArray *newArray = #[#"a",#"c",#"d",#"e",#"f",#"h"];
NSMutableArray *removedArray = [NSMutableArray array];
NSMutableArray *addedArray = [NSMutableArray array];
NSUInteger iold = 0; // index into oldArray
NSUInteger inew = 0; // index into newArray
while (iold < [oldArray count] && inew < [newArray count]) {
// Compare "current" element of old and new array:
NSComparisonResult c = [oldArray[iold] compare:newArray[inew]];
if (c == NSOrderedAscending) {
// oldArray[iold] has been removed
[removedArray addObject:#(iold)];
iold++;
} else if (c == NSOrderedDescending) {
// newArray[inew] has been added
[addedArray addObject:#(inew)];
inew++;
} else {
// oldArray[iold] == newArray[inew]
iold++, inew++;
}
}
// Process remaining elements of old array:
while (iold < [oldArray count]) {
[removedArray addObject:#(iold)];
iold++;
}
// Process remaining elements of new array:
while (inew < [newArray count]) {
[addedArray addObject:#(inew)];
inew++;
}
NSLog(#"removed: %#", removedArray);
NSLog(#"added: %#", addedArray);
Output:
removed: (
1,
4
)
added: (
1,
4,
5
)
I have an array, which is a collection of arrays.
say
(
( 1, x, y, a),
( 2, m, n, o),
( 3, s, t, u, v, w)
)
If I want to get the index of m, how do I get it?
Can that be done using NSMutableArray contains:(id) ?
Check with this code:
int arrayPos = 0;
for (NSArray *tempArray in collectionArray)
{
if(tempArray containsObject:#"m")
{
int indexOfWord = [tempArray indexOfObject:#"m"];
NSlog(#"Array pos: %d Index is : %d",pos, indexOfWord);
}
pos++;
}
In the for loop you get each array that is added in the collection array. After that you are checking the existence of the word in the array. If it is present you are displaying it.
Also printing the position of array also.
The element is situated at:
NSString *str = [[collectionArrayObjectAtIndex:pos] objectAtIndex:indexOfWord];
NSArray *arrWithArr;
NSArray *arrWithM;
NSInteger arrIndex;
for (int i = 0; i< [arrWithArr count]; i++) {
if ([[arrWithArr objectAtIndex:i] containsObject:#"m"]) {
arrIndex = i; break;
}
}
NSInteger mIndex = [[arrWithArr objectAtIndex:arrIndex]indexOfObject:#"m"];
The function you're talking about, "[NSMutableArray contains:]" will tell you if object "B" exists in an array of {A, B, C}.
What you really need to do is come up a second function that does a fast enumeration through your parent array and then does the "contains" thing on the sub-arrays. If it finds "m", return "YES" and the index of that entry in the parent array.