When the array is sorting, [arr sortUsingComparator:sortByTime]
and then I wanna cancel the operation. What should I do?
I don't think this is easily possible. Presumably you are working in a concurrent environment. Then something like this should cause the sort to exit quickly. You need to sync access to the sortCancel BOOL when you set it as well, just as when you get it as below.
__block BOOL sortCancel = NO;
NSObject * lock = NSObject.new; // Sync access to sortCancel
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
#synchronized ( lock ) {
// Check for cancel
if ( sortCancel ) {
// Sort has been cancelled
return (NSComparisonResult)NSOrderedSame;
}
}
// 'Normal' compare, e.g.
if ([obj1 integerValue] > [obj2 integerValue]) {
return (NSComparisonResult)NSOrderedDescending;
}
if ([obj1 integerValue] < [obj2 integerValue]) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
I did not test this, but this should either break the sort or cause it to complete quickly.
I wonder why you want to abort the sorting?
Related
I'm trying to filter AlAssets by year and month. I can already get the dates and filter by year and month, but it's too slow with about 1000 photos. What's the best way to do it?
+ (void) loadFromLibraryByDate:(ALAssetsLibrary *)library assetType:(NSString *)type toArray:(NSMutableArray *)array onTable:(UITableView *)tableView onYear:(NSString *)year onMonth:(NSString *)mouth withDelegate:(id) delegate{
//clean passed objects
[array removeAllObjects];
// filter for the library
NSInteger groupType = ALAssetsGroupAll;
// block to enumerate thought the groups
ALAssetsLibraryGroupsEnumerationResultsBlock listGroupBlock =
^(ALAssetsGroup *group, BOOL *stop){
if(group){
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
if(asset){
// cachedPhotos = [NSMutableDictionary new];
if([asset valueForProperty:ALAssetPropertyType] == type){
if(year != nil && mouth != nil)
{
NSDate *date = [asset valueForProperty:ALAssetPropertyDate];
if(date.year == [year integerValue] && date.month == [mouth integerValue])
{
[array addObject:asset];
}
}
else if(year != nil && mouth == nil)
{
NSDate *date = [asset valueForProperty:ALAssetPropertyDate];
NSString *monthName = [date monthName:date.month];
if(date.year == [year integerValue])
{
if(![array containsObject:monthName])
{
[array addObject:monthName];
}
}
}
else
{
NSDate *date = [asset valueForProperty:ALAssetPropertyDate];
NSNumber *yearNum = [NSNumber numberWithInt:date.year];
if(![array containsObject:yearNum])
{
[array addObject:yearNum];
}
}
}
}
}];
}
else{
if( [delegate respondsToSelector:#selector(didFinishLoadingLibraryByDate:)] ){
[delegate performSelector:#selector(didFinishLoadingLibraryByDate:)];
}
[tableView reloadData];
}
};
// failure block, what happens if when something wrong happens when enumerating
ALAssetsLibraryAccessFailureBlock failBlock = ^(NSError *error){
NSLog(#"Error: %#", [error localizedDescription]);
static dispatch_once_t pred;
dispatch_once(&pred, ^{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *libraryFailure = [[UIAlertView alloc] initWithTitle:#"Serviço de Localização" message:#"Para poder partilhar conteúdos nesta versão iOS, tem de autorizar os serviços de localização. (Definições > Serviços de Localização)" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[libraryFailure show];
[libraryFailure release];
});
});
};
[library enumerateGroupsWithTypes:groupType usingBlock:listGroupBlock failureBlock:failBlock];
Any help appreciated, thanks
I think you're on the right track. There is no way I know of to filter on metadata except by enumerating the way you are doing it. Unfortunately, enumerating through asset groups is just inherently slow on iOS -- if you think 1000 is bad, try 10k or 20k assets (not at all uncommon, I have that on my carry phone right now).
One way around this (not necessarily advised, as it's a lot of work and the bug potential is very high) is to build your own database of asset timestamps. While the user is otherwise busy (with a tutorial or something), enumerate over all the assets and copy the metadata and ALAssetPropertyAssetURL to whatever format works best for you. Don't forget to listen for ALAssetsLibraryChangedNotification messages if you do this.
You should enumerate all ALAsset first When App launching,and then filter them. Because Enumerating ALAsset from database is so slow, so you should not reenumerate them again.
Therer is a notice, reenumerate ALAsset more faster then the first. Apple should optimize the library.
I have been working with this method for hours. It is supposed to simply check if the attribute of the cards passed in as otherCards isEqual to the same attribute of this (self) instance of class. But I am tearing my hair out - it is giving false results
(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSArray *values = [NSArray arrayWithArray:[otherCards valueForKey:key]];
int countUnequalMatches = 0;
for (NSNumber *setValue in values) {
if (![[self valueForKey:key] isEqual:setValue]) {
countUnequalMatches++;}
}
NSLog(#"countUnequalMatches %d values count: %d", countUnequalMatches, [values count]);
if (countUnequalMatches == [values count]) return YES ?: NO;
}
It is called like this:
if ([self otherCards:otherCards allHaveUnequalAttribute:CARD_SHAPE]) {
NSLog(#"All unequal");
} else {
NSLog(#"One or more card equal");
}
I found the bug myself
if (countUnequalMatches == [values count]) return YES ?: NO;
should be:
if (countUnequalMatches == [otherCards count]) return YES ?: NO;
as I used the Set as an way to count unique items.
This is the sort of thing that predicates are very well suited to handle.
-(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K == %#", key, [self valueForKey:key]];
NSArray *equalCards = [otherCards filteredArrayUsingPredicate:pred];
return equalCards.count == 0;
}
How would you stop fast enumeration once you have gotten what your looking for.
In a for loop I know you just set the counter number to like a thousand or something.
Example:
for (int i=0;i<10;i++){
if (random requirement){
random code
i=1000;
}
}
so without converting the fast enumeration into a forward loop type thing (by comparing i to the [array count] how can you stop a fast enumeration in the process?
from the docs
for (NSString *element in array) {
if ([element isEqualToString:#"three"]) {
break;
}
}
if you want to end enumeration when a certain index is reached, block-based enumeration might be better, as it give you the index while enumerating:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//…
if(idx == 1000)
*stop = YES;
}];
for (id object in collection) {
if (condition_met) {
break;
}
}
Couldn't you just use a break statement?
for (int x in /*your array*/){
if (random requirement){
random code
break;
}
}
Just adding that for nested loops, a break on an inner loop breaks just that loop. Outer loops will continue. If you want to break out completely you could do so like this:
BOOL flag = NO;
for (NSArray *array in arrayOfArrays) {
for (Thing *thing in array) {
if (someCondition) {
// maybe do something here
flag = YES;
break;
}
}
if (flag) {
break;
}
}
I'm trying to sort an array of version numbers that might look like this properly sorted:
1.0.1
1.1.2
1.2.0
1.10.3
2.1.1
2.2.2
2.10.2
I have an array and I do the following:
NSArray *allUpdatekeys = [allUpdatekeystmp sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
This produces a list like:
1.0.1
1.1.2
1.10.3
1.2.0
...
So I tried writing a custom selector and I got this far by looking at examples I found:
- (NSComparisonResult)versionCompare:(NSString*) secondVersion {
NSArray *secondVer = [secondVersion componentsSeparatedByString:#"."];
NSString *firstVersion = [];
NSArray *firstVer = [firstVersion componentsSeparatedByString:#"."];
NSUInteger firstPri = [firstVer[0] integerValue];
NSUInteger firstSec = [firstVer[1] integerValue];
NSUInteger firstTer = [firstVer[2] integerValue];
NSUInteger secondPri = [secondVer[0] integerValue];
NSUInteger secondSec = [secondVer[1] integerValue];
NSUInteger secondTer = [secondVer[2] integerValue];
NSLog(#"In the version compare method");
NSLog(#"first %# second %# ", self, secondVersion);
if (firstPri < secondPri) {
return NSOrderedAscending;
}
else if (firstPri secondPri) {
return NSOrderedDescending;
} else {
if (firstSec < secondSec) {
return NSOrderedAscending;
}
else if (firstSec secondSec) {
return NSOrderedDescending;
}
else {
if (firstTer < secondTer) {
return NSOrderedAscending;
}
else if (firstTer secondTer) {
return NSOrderedDescending;
}
else {
return NSOrderedSame;
}
}
} }
My question: What is the way to reference the first version? I need what essentially is the first string from the array. I have been racking my brain to understand this, but all of the examples I have found don't work either. I'm using Xcode 4.4 on 10.8 if that helps or matters.
I think localizedStandardCompare is what you're looking for:
NSArray *theArray = [NSArray arrayWithObjects:
#"1.0.1",
#"1.1.2",
#"1.2.0",
#"1.10.3",
#"2.1.1",
#"2.2.2",
#"2.10.2",
nil];
NSArray *sortedArray = [[NSArray alloc] initWithArray:[theArray sortedArrayUsingSelector:#selector(localizedStandardCompare:)]];
NSLog(#"%#", sortedArray);
Output:
(
"1.0.1",
"1.1.2",
"1.2.0",
"1.10.3",
"2.1.1",
"2.2.2",
"2.10.2"
)
Another solution to get a sorted array in which 1.2.0 is less than 1.10.3, which means that
the array is numerically sorted, is:
allUpdatekeys = [allUpdatekeystmp sortedArrayUsingFunction:cmp context:NULL];
with the function:
NSComparisonResult cmp( id val1, id val2, void *context ){
return [val1 compare:num2 options:NSNumericSearch | NSCaseInsensitiveSearch];
}
I have a bunch of UITextFields where a user must enter a number (the keyboard is the number pad) before pressing a submit button. When the submit button is pressed, a "check" method is called that wants to check if the data is valid... aka not empty. Now this would be easy if it was just one textField but I have 14. I want a simple if-statement that checks if one of the fields or all of the fields are empty....
I have the follow UITextFields declared: number1, number2, number3 etc etc
and I have the follow strings declared which can take the value of the UITextField.text... they are declared: temp1, temp2, temp3 etc...
how would I go about making a pseudocode if statement like the one below?
Thanks
if (!valid)
{
NSLog (#"not valid)
}
else
{
//proceed to next method
}
I am supposing UITextField variable as *tfield so here is the solution
if (tfield.text.length > 0 || tfield.text != nil || ![tfield.text isEqual:#""])
{
//do your work
}
else
{
//through error
}
or you could just call
[myTextField hasText];
which will return NO if the field is empty.
I think something like this is what you want. It's just using isEqualToString: to check if the string is empty.
NSString *temp1 = number1.text;
NSString *temp2 = number2.text;
... ///< temp3, etc
if ([temp1 isEqualToString:#""]) {
// temp1 not valid
} else if ([temp2 isEqualToString:#""]) {
// temp2 not valid
} ... {
// temp3, etc
} else {
// Valid
}
You may want to trim whitespace characters when grabbing temp1 so that #" " would also be blank. For that, take a look at NSString's stringByTrimmingCharactersInSet: method like so:
NSString *temp1 = [number1.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
Update:
If you want to do it with an array you could do something like:
NSMutableArray *array = [NSMutableArray arrayWithCapacity:0];
[array addObject:number1.text];
[array addObject:number2.text];
... ///< number3, etc
BOOL ok = YES;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isEqualToString:#""]) {
ok = NO;
*stop = YES;
}
}];
if (ok) {
// Valid
} else {
// Not valid
}
Something like this would iterate through your UITextFields:
[self.view.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UITextField class]]) {
// do your checks
}
}];
you can put like this
if(![temp1 isEqualToString:#""] && ![temp2 isEqualToString:#""] ... ![temp14 isEqualToString:#""])
{
NSLog (#" valid);
}
else
{NSLog (#"not valid);
}
In Swift,
You can use this block of code.
if textField.text?.isEmpty {
//Return true if the textfield is empty or false if it's not empty.
}