I have several thousand strings in the form "verb-noun" for which I want to extract the noun portion. I am looking for the FASTEST way to do this. The verb and noun portions can be any length.
I have tried
NSString *noun = [[verb componentsSeparatedByString:#"-"] lastObject];
Which is slow... For my list of over 3000 entries it takes about 3 seconds.
Also tried
NSString *noun = [verb substringFromIndex:[verb rangeOfString:#"-"].location + 1];
which is MUCH faster, about a half second.
Anyone have suggestions for making this even faster?
If your work with these strings is thread-safe then one option is to use GCD to iterate over multiple verb values simultaneously, bringing multiple cores into action. Use dispatch_apply instead of whatever loop you're using, something like:
dispatch_apply([myWordArray count], queue, ^(size_t i) {
NSString *verb = [myWordArray objectAtIndex:i];
NSString *noun = [verb substringFromIndex:[verb rangeOfString:#"-"].location + 1];
// do something with noun...
});
Just keep in mind that this will do more than one pass simultaneously, so be very sure about threading issues.
Fastest way would probably to sort the most likely cases for where the hyphen is, and then check for those first without using a loop. For example, if the most likely cases for index of hyphen are 5, 4, 6, 7, 3, 2 in that order, you could do this:
NSString * verb = #"verb-noun";
NSString * noun = nil;
//use do...while(0) to avoid nested if else
do
{
if([verb characterAtIndex:5] == '-')
{
noun = [verb substringFromIndex:6];
break;
}
if([verb characterAtIndex:4] == '-')
{
noun = [verb substringFromIndex:5];
break;
}
if([verb characterAtIndex:6] == '-')
{
noun = [verb substringFromIndex:7];
break;
}
if([verb characterAtIndex:7] == '-')
{
noun = [verb substringFromIndex:8];
break;
}
if([verb characterAtIndex:3] == '-')
{
noun = [verb substringFromIndex:4];
break;
}
if([verb characterAtIndex:2] == '-')
{
noun = [verb substringFromIndex:4];
break;
}
} while(0);
//if not one of most likely cases, loop
if(!noun)
{
for(int j = 8; j < verb.length; j++)
{
if([verb characterAtIndex:j] == '-')
{
noun = [verb substringFromIndex:j + 1];
break;
}
}
}
if(noun)
{
//noun should never be nil
NSLog(#"found the noun");
}
Related
I'm currently working on the CS50 Speller function. I have managed to compile my code and have finished a prototype of the full program, however it does not work (it doesn't recognise any mispelled words). I am looking through my functions one at a time and printing out their output to have a look at what's going on inside.
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
char word[LENGTH + 1];
int counter = 0;
FILE *dicptr = fopen(dictionary, "r");
if (dicptr == NULL)
{
printf("Could not open file\n");
return 1;
}
while (fscanf(dicptr, "%s", word) != EOF)
{
printf("%s", word);
node *n = malloc(sizeof(node));
if (n == NULL)
{
unload();
printf("Memory Error\n");
return false;
}
strcpy(n->word, word);
int h = hash(n->word);
n->next = table[h];
table[h] = n;
amount++;
}
fclose(dicptr);
return true;
}
From what I can see this works fine. Which makes me wonder if the issue is with my check function as shown here:
bool check(const char *word)
{
int n = strlen(word);
char copy[n + 1];
copy[n] = '\0';
for(int i = 0; i < n; i++)
{
copy[i] = tolower(word[i]);
printf("%c", copy[i]);
}
printf("\n");
node *cursor = table[hash(copy)];
while(cursor != NULL)
{
if(strcasecmp(cursor->word, word))
{
return true;
}
cursor = cursor->next;
}
return false;
}
If someone with a keener eye can spy what is the issue I'd be very grateful as I'm stumped. The first function is used to load a the words from a dictionary into a hash table\linked list. The second function is supposed to check the words of a txt file to see if they match with any of the terms in the linked list. If not then they should be counted as incorrect.
This if(strcasecmp(cursor->word, word)) is a problem. From man strcasecmp:
Return Value
The strcasecmp() and strncasecmp() functions return an
integer less than, equal to, or greater than zero if s1 (or the first
n bytes thereof) is found, respectively, to be less than, to match, or
be greater than s2.
If the words match, it returns 0, which evaluates to false.
I know how to add custom functions to NSNumber for NSExpression to work with it. But for use it i need to declarate a string like "FUNCTION(1, 'sin')". Is is any way to declarate it just like "sin(1)"?
No, you cannot extend the syntax understood by NSExpression(format:).
For advanced expression parsing and evaluating, use 3rd party solutions
such as DDMathParser.
The selected answer is, in my opinion, ridiculous. You can, of course, simply reformat your string to your desired custom function, no need to become dependent on an entire library.
In your case, something like the following would work just fine.
NSString *equation = #"2+sin(54.23+(2+sin(sin(3+5))))-4+(5-3)+cos(4)";//your equation here
NSArray *functionNames = #[#"sin", #"cos", #"tan"];//your supported functions here
for (NSString *functionName in functionNames) {
NSString *functionPrefix = [NSString stringWithFormat:#"%#(", functionName];
while ([equation containsString:functionPrefix]) {
int parensLevel = 1;
int functionParameterIndex = ((int)[equation rangeOfString:functionPrefix].location)+((int)functionPrefix.length);
int characterIndex = functionParameterIndex;
while (characterIndex < equation.length) {
NSString *character = [equation substringWithRange:NSMakeRange(characterIndex, 1)];
if ([character isEqualToString:#"("]) {
parensLevel++;
} else if ([character isEqualToString:#")"]) {
parensLevel--;
}
if (parensLevel == 0) {
break;
}
characterIndex++;
}
if (parensLevel != 0) {
break;//parens weren't balanced, error handle as needed
}
NSString *functionParameter = [equation substringWithRange:NSMakeRange(functionParameterIndex, characterIndex-functionParameterIndex)];
NSString *function = [NSString stringWithFormat:#"%#(%#)", functionName, functionParameter];
equation = [equation stringByReplacingOccurrencesOfString:function withString:[NSString stringWithFormat:#"FUNCTION(%#,'%#')", functionParameter, functionName]];
}
}
//po string = "2+FUNCTION(54.23+(2+FUNCTION(FUNCTION(3+5,'sin'),'sin')),'sin')-4+(5-3)+FUNCTION(4,'cos')"
I wrote this in Objective-C but it works converted to swift as well.
I need to validate a password with these rules:
Password must have 6-12 characters
and at least two of the following:
Uppercase letters, Lowercase letters, Numbers or Symbols.
Below is my current regex:
^((?=.*[a-z])|(?=.*[A-Z])|(?=.*\\d)|(?=.*(_|[-+_!##$%^&*.,?]))).{6,12}
I am struggling about how to make the 'at least' condition.
You may define a function to check your requirements one by one and increment a counter to see how many of them actually are met. If more than 1 matched and the string length is between 6 and 12, the password passes.
NSUInteger checkPassword(NSString * haystack) {
NSArray * arr = [NSArray arrayWithObjects: #"(?s).*\\d.*", #"(?s).*[a-z].*", #"(?s).*[A-Z].*", #"(?s).*[-+_!##$%^&*.,?].*",nil];
NSUInteger cnt = 0;
for (NSUInteger index = 0; index < [arr count]; ++index) {
NSPredicate * passTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", [arr objectAtIndex:index]];
if ([passTest evaluateWithObject:haystack]) {
cnt = cnt + 1;
}
}
if (cnt > 1 && [haystack length] > 5 && [haystack length] < 13)
{
return 1;
}
else
{
return 0;
}
}
And a sample IDEONE demo:
NSString * s = #"123DF4ffg";
NSLog(#"Result: %d", checkPassword(s));
// => Result: 1
Note that it is possible to write a single pattern for this, but it will be rather long and clumsy as you need to define all possible pairs of lookahead alternatives.
you can try with this
^(?:(?=.*[a-z])(?:(?=.*[A-Z])(?=.*[\\d\\w])|(?=.*\\w)(?=.*\\d))|(?=.*\\w)(?=.*[A-Z])(?=.*\\d)).{6,12}$;
I have some data in an NSString, separated by colons:
#"John:Doe:1970:Male:Dodge:Durango"
I need to limit the total length of this string to 100 characters. But I also need to ensure the correct number of colons are present.
What would be a reasonable to way to truncate the string but also add the extra colons so I can parse it into the correct number of fields on the other side?
For example, if my limit was 18, you would end up with something like this:
#"John:Doe:1970:Ma::"
Here's an updated version of my own latest pass at this. Uses #blinkenlights algorithm:
+ (NSUInteger)occurrencesOfSubstring:(NSString *)substring inString:(NSString *)string {
// http://stackoverflow.com/a/5310084/878969
return [string length] - [[string stringByReplacingOccurrencesOfString:substring withString:#""] length] / [substring length];
}
+ (NSString *)truncateString:(NSString *)string toLength:(NSUInteger)length butKeepDelmiter:(NSString *)delimiter {
if (string.length <= length)
return string;
NSAssert(delimiter.length == 1, #"Expected delimiter to be a string containing a single character");
int numDelimitersInOriginal = [[self class] occurrencesOfSubstring:delimiter inString:string];
NSMutableString *truncatedString = [[string substringToIndex:length] mutableCopy];
int numDelimitersInTruncated = [[self class] occurrencesOfSubstring:delimiter inString:truncatedString];
int numDelimitersToAdd = numDelimitersInOriginal - numDelimitersInTruncated;
int index = length - 1;
while (numDelimitersToAdd > 0) { // edge case not handled here
NSRange nextRange = NSMakeRange(index, 1);
index -= 1;
NSString *rangeSubstring = [truncatedString substringWithRange:nextRange];
if ([rangeSubstring isEqualToString:delimiter])
continue;
[truncatedString replaceCharactersInRange:nextRange withString:delimiter];
numDelimitersToAdd -= 1;
}
return truncatedString;
}
Note that I don't think this solution handles the edge case from CRD where the number of delimiters is less than the limit.
The reason I need the correct number of colons is the code on the server will split on colon and expect to get 5 strings back.
You can assume the components of the colon separated string do not themselves contain colons.
Your current algorithm will not produce the correct result when one or more of the characters among the last colonsToAdd is a colon.
You can use this approach instead:
Cut the string at 100 characters, and store the characters in an NSMutableString
Count the number of colons, and subtract that number from the number that you need
Starting at the back of the string, replace non-colon characters with colons until you have the right number of colons.
I tend towards #dasblinkenlight, it's just an algorithm after all, but here's some code. Few modern shorthands - used an old compiler. ARC assumed. Won't claim it's efficient, or beautiful, but it does work and handles edge cases (repeated colons, too many fields for limit):
- (NSString *)abbreviate:(NSString *)input limit:(NSUInteger)limit
{
NSMutableArray *fields = [[input componentsSeparatedByString:#":"] mutableCopy];
NSUInteger colonCount = fields.count - 1;
if (colonCount >= limit)
return [#"" stringByPaddingToLength:limit withString:#":" startingAtIndex:0];
NSUInteger nonColonsRemaining = limit - colonCount;
for (NSUInteger ix = 0; ix <= colonCount; ix++)
{
if (nonColonsRemaining > 0)
{
NSString *fieldValue = [fields objectAtIndex:ix];
NSUInteger fieldLength = fieldValue.length;
if (fieldLength <= nonColonsRemaining)
nonColonsRemaining -= fieldLength;
else
{
[fields replaceObjectAtIndex:ix withObject:[fieldValue substringToIndex:nonColonsRemaining]];
nonColonsRemaining = 0;
}
}
else
[fields replaceObjectAtIndex:ix withObject:#""];
}
return [fields componentsJoinedByString:#":"];
}
I have instructions to make a prefix method that takes two strings for each position where mask = 0 and the first string = second string up until these conditions are not meet that is your prefix NSString.
I made my attempt but for some reason my prefix string is returning as null and I was hoping i could get some help.
here is my method
- (void)prefixCalculation:(NSString *)seriesStart SeriesEnd:(NSString *)seriesEnd {
// call this method when loading the view to get everything set up
NSLog(#"start %#", seriesStart);
NSLog(#"end %#", seriesEnd);
// allocate values so you can use this to create the UITextField
seriesStartString = seriesStart;
seriesEndString = seriesEnd;
// set prefix string
for (int i = 0; i <= seriesStartString.length ; i++) {
unichar c1 = [seriesStartString characterAtIndex:i];
unichar c2 = [seriesEndString characterAtIndex:i];
if (c1 != c2) {
break;
}
else if (c1 == c2) {
NSString *str = [NSString stringWithFormat: #"%C", c1];
[prefixString appendFormat:#"%#",str];
}
}
NSLog(#"prefix %#", prefixString);
}
I am not sure what I am doing wrong but prefixString which is a NSMutableStrong comes back as null, any help would be greatly appreciated.
Since in your code you don't show the initialization of prefixString, I take a guess and suggest you to check whether you initialized it or not.
If that's not the case, prefixString is nil and sending messages to it will fail silently.