I want to search through an array with strings in it. Therefore i use a searchbar.
Here's my code:
for(NSString *string in itemsArray)
{
NSRange nameRange = [string rangeOfString:text options:NSCaseInsensitiveSearch];
if (nameRange.location != NSNotFound)
{
[filteredItemsArray addObject:string];
}
}
It works great, but the problem is it just finds the string when I write it exactly the same way (except the case insensitivity).
But I would also like to find it even if the strings doesn't fit a 100%. For example when I search for 'Ralle Turner' it should also find the string with 'Ralle/Ralph Turner'.
Thanks for your help guys!
You need to split the search string into words, and then iterate through each word to find a match.
You can split the string using this code inside your loop
for(NSString *string in itemsArray)
{
NSArray *words = [text componentsSeparatedByString:#" "];
for (NSString *word in words)
{
NSRange nameRange = [string rangeOfString:word options:NSCaseInsensitiveSearch];
if (nameRange.location != NSNotFound)
{
[filteredItemsArray addObject:string];
break;
}
}
}
You can compute the levenshtein distance of two string to see if there is a potential match.
You can easily find some implementation on the web, for instance:
-via NSString Category
-via a single method
Or use one the others approximate string matching algorithms
Related
I have a long NSString e.g. "For performance reasons, a table view data source generally reuses UITableViewCell objects. The table view maintains a queue of cell objects that are marked for reuse. For example, if you were displaying a list that contained a 1000 rows it would be highly inefficient to create 1000 table view cells, whereas a lot of more effective to create the few currently in view."
Now if user search for word "maintains" then above NSString definitely have this word.. now i just want to show only rand of line with this word, not whole text. So the use will know that this text have this word and where it is.
-User search for "maintains".
-code process the NSString.
-Output will look like this.. "table view maintains a queue of cell objects that..." or this can be also a result.. "maintains a queue of cell objects that..."
2nd result would be great...
NSRange range = [myString rangeOfString:#"maintains" options:NSBackwardsSearch range:NSMakeRange(0, 11)];
NSLog(#"range.location: %lu", range.location);
NSString *substring = [myString substringFromIndex:range.location+1];
NSLog(#"substring: '%#'", substring);`
but no success...from this CODE
Please help me to get my target...
This will do it:
- (NSString *)getSnippetContaining:(NSString *)keyword
inString:(NSString *)theString
numberOfWords:(NSUInteger)wordCount
{
NSRange range = [theString rangeOfString:keyword
options:NSBackwardsSearch
range:NSMakeRange(0, theString.length)];
if (range.location == NSNotFound) {
return nil;
}
NSString *substring = [theString substringFromIndex:range.location];
NSArray *words = [substring componentsSeparatedByString:#" "];
if (wordCount > words.count) {
wordCount = words.count;
}
NSArray *snippetWords = [words subarrayWithRange:NSMakeRange(0, wordCount)];
NSString *snippet = [snippetWords componentsJoinedByString:#" "];
return snippet;
}
numberOfWords is the length of the snippet you wish to extract from the input.
I have a block of text (a newspaper article if it's of any relevance) was wondering if there is a way to extract all sentences containing a particular keyword in objective-c? I've been looking a bit at ParseKit but aren't having much luck!
You can enumerate sentences using native NSString methods like this...
NSString *string = #"your text";
NSMutableArray *sentences = [NSMutableArray array];
[string enumerateSubstringsInRange:NSMakeRange(0, string.length)
options:NSStringEnumerationBySentences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
//check that this sentence has the string you are looking for
NSRange range = [substring rangeOfString:#"The text you are looking for"];
if (range.location != NSNotFound) {
[sentences addObject:substring];
}
}];
for (NSString *sentence in sentences) {
NSLog(#"%#", sentence);
}
At the end you will have an array of sentences all containing the text you were looking for.
Edit: As noted in the comments there are some inherit weaknesses with my solution as it requires a perfectly formatted sentence where period + space is only used when actually ending sentences... I'll leave it in here as it could be viable for people sorting a text with another (known) separator.
Here's another way of achieving what you want:
NSString *wordYouAreLookingFor = #"happy";
NSArray *arrayOfSentences = [aString componentsSeparatedByString:#". "]; // get the single sentences
NSMutableArray *sentencesWithMatchingWord = [[NSMutableArray alloc] init];
for (NSString *singleSentence in arrayOfSentences) {
NSInteger originalSize = [singleSentence length];
NSString *possibleNewString = [singleSentence stringByReplacingOccurrencesOfString:wordYouAreLookingFor withString:#""];
if (originalSize != [possibleNewString length]) {
[sentencesWithMatchingWord addObject:singleSentence];
}
}
Hi I'm looking to compare 2 strings using objective c. One string is a single word and the other will be one or more words. How can I cheeck for a match at word level? I.e if any word from the multiple word string matches my single word string return a 1 else return a 0 ? Any help much appreciated.
You might be tempted to use -[NSString rangeOfString:]:
if ([multipleWords rangeOfString:singleWord].location != NSNotFound)
return YES;
return NO;
But it's imperfect. You could have "returning" in multipleWords and singleWord could be "return", giving you a false positive.
So instead we must use NSRegularExpression.
NSString *single = #"returning";
NSString *multiple = #"a man is returning home";
NSString *pattern = [NSString stringWithFormat:#"\\b%#\\b",single];
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:&error];
NSRange range = NSMakeRange(0, multiple.length);
NSUInteger matched = [regex numberOfMatchesInString:multiple options:0 range:range];
NSLog(#"number of matched = %ld", matched);
You can use NSString's rangeOfString method:
NSRange range = [multiWordString rangeOfString:oneWordString];
if (range.location == NSNotFound)
return 0;
else
return 1;
The above code will return 1 if you are searching for "WAR" inside the longer "I AM AWARE". Maybe you're looking for entire word match.
In that case you should split the long string into an array of single words using...
NSArray *singleWords = [multiWordString componentsSeparatedByString: #" "];
Then parse the array to find the oneWordString
You can split up words in a string into an array using [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]. You can then check each word in the array to see if it matches the single word being searched. It's not the most efficient way, but it could work for your needs.
I know how to find a string in another string, that is easy. But in this case I want to find John Smith within the allProfessors string. So I figured I could just split the string and search for both parts, which works how I want:
NSString *fullName = #"John Smith";
NSArray *parts = [fullName componentsSeparatedByString:#" "];
NSString *allProfessors = #"Smith, John; Clinton, Bill; Johnson, John";
NSRange range = [[allProfessors lowercaseString] rangeOfString:[[parts objectAtIndex:0] lowercaseString]];
NSRange range2 = [[allProfessors lowercaseString] rangeOfString:[[parts objectAtIndex:1] lowercaseString]];
if(range.location != NSNotFound && range2.location != NSNotFound) {
NSLog(#"Found");
} else {
NSLog(#"Not Found");
}
What I want to know is, is this the BEST way to do this or is there a more preferred method to do what I want?
In addition to this, what if my fullName is longer than my allProfessors name, such as:
NSString *fullName = #"Gregory Smith";
NSString *allProfessors = #"Smith, Greg; Clinton, Bill; Johnson, John";
I still want there to be a match for Greg Smith and Gregory Smith.
You could use regular expressions, which I prefer to use. See RegexKitLite.
With RegexKitLite, you could use a regular expression like (untested):
NSString *regEx = #"(?i)Smith,\\s*\\w";
NSArray *matchingStrings = [allProfessors componentsMatchedByRegex:regEx];
if ([matchingStrings count] == 0) // not found!
{
[...]
}
else
{
[...]
}
Using RegexKitLite you could alternatively have used [NSString stringByMatching:(NSString*)].
You can really do a lot with regular expression. There are a ton of different functions available through Using RegexKitLite. The regular expression above should find people with the last name of Smith.
Regular Expression explained:
(?i) make this case insensitive
Smith matches last name of Smith. Obviously you could change this to anything
, match a comma
\\s* match any number of spaces (greedy)
\\w match a word
Also, you could use [NSString rangeOfString:options:] function like:
if ([myString rangeOfString:#"John" options:NSCaseInsensitiveSearch].location != NSNotFound &&
[myString rangeOfString:#"Smith" options:NSCaseInsensitiveSearch].location != NSNotFound)
{
NSLog(#"Found");
}
else
{
NSLog(#"Not Found");
}
Also see similar functions like [rangeOfString:options:range:locale:] so that you can do case insensitive searches and even specify a locale.
I've already found how to capitalize all words of the sentence, but not the first word only.
NSString *txt =#"hi my friends!"
[txt capitalizedString];
I don't want to change to lower case and capitalize the first char. I'd like to capitalize the first word only without change the others.
Here is another go at it:
NSString *txt = #"hi my friends!";
txt = [txt stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[txt substringToIndex:1] uppercaseString]];
For Swift language:
txt.replaceRange(txt.startIndex...txt.startIndex, with: String(txt[txt.startIndex]).capitalizedString)
The accepted answer is wrong. First, it is not correct to treat the units of NSString as "characters" in the sense that a user expects. There are surrogate pairs. There are combining sequences. Splitting those will produce incorrect results. Second, it is not necessarily the case that uppercasing the first character produces the same result as capitalizing a word containing that character. Languages can be context-sensitive.
The correct way to do this is to get the frameworks to identify words (and possibly sentences) in the locale-appropriate manner. And also to capitalize in the locale-appropriate manner.
[aMutableString enumerateSubstringsInRange:NSMakeRange(0, [aMutableString length])
options:NSStringEnumerationByWords | NSStringEnumerationLocalized
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[aMutableString replaceCharactersInRange:substringRange
withString:[substring capitalizedStringWithLocale:[NSLocale currentLocale]]];
*stop = YES;
}];
It's possible that the first word of a string is not the same as the first word of the first sentence of a string. To identify the first (or each) sentence of the string and then capitalize the first word of that (or those), then surround the above in an outer invocation of -enumerateSubstringsInRange:options:usingBlock: using NSStringEnumerationBySentences | NSStringEnumerationLocalized. In the inner invocation, pass the substringRange provided by the outer invocation as the range argument.
Use
- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
and capitalize the first object in the array and then use
- (NSString *)componentsJoinedByString:(NSString *)separator
to join them back
pString = [pString
stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:[[pString substringToIndex:1] capitalizedString]];
you can user with regular expression i have done it's works for me simple you can paste below code
+(NSString*)CaptializeFirstCharacterOfSentence:(NSString*)sentence{
NSMutableString *firstCharacter = [sentence mutableCopy];
NSString *pattern = #"(^|\\.|\\?|\\!)\\s*(\\p{Letter})";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL];
[regex enumerateMatchesInString:sentence options:0 range:NSMakeRange(0, [sentence length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
//NSLog(#"%#", result);
NSRange r = [result rangeAtIndex:2];
[firstCharacter replaceCharactersInRange:r withString:[[sentence substringWithRange:r] uppercaseString]];
}];
NSLog(#"%#", firstCharacter);
return firstCharacter;
}
//Call this method
NsString *resultSentence = [UserClass CaptializeFirstCharacterOfSentence:yourTexthere];
An alternative solution in Swift:
var str = "hello"
if count(str) > 0 {
str.splice(String(str.removeAtIndex(str.startIndex)).uppercaseString, atIndex: str.startIndex)
}
For the sake of having options, I'd suggest:
NSString *myString = [NSString stringWithFormat:#"this is a string..."];
char *tmpStr = calloc([myString length] + 1,sizeof(char));
[myString getCString:tmpStr maxLength:[myString length] + 1 encoding:NSUTF8StringEncoding];
int sIndex = 0;
/* skip non-alpha characters at beginning of string */
while (!isalpha(tmpStr[sIndex])) {
sIndex++;
}
toupper(tmpStr[sIndex]);
myString = [NSString stringWithCString:tmpStr encoding:NSUTF8StringEncoding];
I'm at work and don't have my Mac to test this on, but if I remember correctly, you couldn't use [myString cStringUsingEncoding:NSUTF8StringEncoding] because it returns a const char *.
In swift you can do it as followed by using this extension:
extension String {
func ucfirst() -> String {
return (self as NSString).stringByReplacingCharactersInRange(NSMakeRange(0, 1), withString: (self as NSString).substringToIndex(1).uppercaseString)
}
}
calling your string like this:
var ucfirstString:String = "test".ucfirst()
I know the question asks specifically for an Objective C answer, however here is a solution for Swift 2.0:
let txt = "hi my friends!"
var sentencecaseString = ""
for (index, character) in txt.characters.enumerate() {
if 0 == index {
sentencecaseString += String(character).uppercaseString
} else {
sentencecaseString.append(character)
}
}
Or as an extension:
func sentencecaseString() -> String {
var sentencecaseString = ""
for (index, character) in self.characters.enumerate() {
if 0 == index {
sentencecaseString += String(character).uppercaseString
} else {
sentencecaseString.append(character)
}
}
return sentencecaseString
}