Converting uppercase string to title case in Objective-C - objective-c

I created the following method which starts by using the built-in convertStringToTitleCase method on NSString but it really just capitalizes the first letter of each word. I see in .NET there is a method for TextInfo.ToTitleCase which attempts what I'd like to do with Objective-C but also falls short.
http://msdn.microsoft.com/en-us/library/system.globalization.textinfo.totitlecase.aspx
The method I wrote to start is below. How would you handle properly casing an uppercase string? Would a database of words to convert to all uppercase/lowercase help?
- (NSString *)convertStringToTitleCase:(NSString *)str {
NSMutableString *convertedStr = [NSMutableString stringWithString:[str capitalizedString]];
NSRange range = NSMakeRange(0, convertedStr.length);
// a list of words to always make lowercase could be placed here
[convertedStr replaceOccurrencesOfString:#" De "
withString:#" de "
options:NSLiteralSearch
range:range];
// a list of words to always make uppercase could be placed here
[convertedStr replaceOccurrencesOfString:#" Tv "
withString:#" TV "
options:NSLiteralSearch
range:range];
return convertedStr;
}

As noted in comments, the .NET method you refer to doesn't do "proper" title case (that is, follow a list of exception words to be left in either all-caps or all-lowercase), so -[NSString capitalizedString] is as equivalent as you'll get. If you want exception words, you'll have to write your own method (or find someone else who did, as a google search for NSString "title case" might).
How "proper" your title casing gets depends on how many exception words you're willing to throw at it. How much of the English language do you want it to support? What about other languages? It'll also depend on how far you go in analyzing word boundaries -- you might want "TV" to stay all-caps regardless of whether it's in quotes, at the end of a sentence, etc., but you probably also don't want "you've" to come out "You'Ve".
If you want to process exception words, your plan of repeatedly running replaceOccurrencesOfString... will get slower the more exception words you have. (Also, using spaces in your search/replace strings means you aren't considering other word boundaries you might want to.)
It might be useful to consider NSRegularExpression, since regular expressions already have pretty robust notions of case and word boundaries. If that doesn't work well for you, using a scanner to read through the input string while producing a transformed output string would be more efficient than running multiple search/replace operations.

A nice one-liner(not a general solution, probably very inefficient on huge strings):
[[str lowercaseString] capitalizedString];

extension String {
/**
Get the title case string.
*/
var titleCase: String {
get {
return getTitleCaseString()
}
}
// MARK: Private methods.
/**
Get title case string.
- returns: The title case string regarding the lowercase words.
*/
private func getTitleCaseString() -> String {
struct Holder {
static let lowercaseWords = ["a", "an", "and", "at", "but", "by", "else", "for",
"from", "if", "in", "into", "is", "nor", "of", "off",
"on", "or", "out", "the", "to", "via", "vs", "with"]
}
return replaceToLowercaseAllOccurrencesOfWords(Holder.lowercaseWords).capitalizeFirst
}
/**
Replace to lowercase all occurrences of lowercase words.
- parameter lowercaseWords: The lowercase words to replace.
- returns: String with all occurrences replace to the lowercase words.
*/
private func replaceToLowercaseAllOccurrencesOfWords(lowercaseWords: [String]) -> String {
let capitalizedSelf = NSMutableString(string: self.capitalizedString)
for word in lowercaseWords {
if let lowercaseWordRegex = try? NSRegularExpression(pattern: "\\b\(word)\\b", options: .CaseInsensitive) {
lowercaseWordRegex.replaceMatchesInString(capitalizedSelf,
options: NSMatchingOptions(),
range: NSMakeRange(0, capitalizedSelf.length),
withTemplate: word)
}
}
return capitalizedSelf as String
}
/**
Capitalize first char.
*/
private var capitalizeFirst: String {
if isEmpty { return "" }
var result = self
result.replaceRange(startIndex...startIndex, with: String(self[startIndex]).uppercaseString)
return result
}
}

Related

How do I replace letters with numbers using the replace function in kotlin inside a lambda expression

mood = "leet"
modifier = { message ->
val regex = """(L|e|t)""".toRegex()
//Clueless about what to do after this
}
THIS IS WHAT I CAME UP WITH SO FAR, THE QUESTION IN THE BOOK BIG NERD RANCH KOTLIN EDITION 2 SAYS "leet (or 1337): The narrator will speak in leetspeak, replacing letters with numbers and symbols that look similar. For example, ‘L’ becomes ‘1’; ‘E’ becomes ‘3’; ‘T’ becomes ‘7’. (Hint: Take a look at String’s replace function. There is a version that accepts a lambda as the second parameter.)"
This is the function they're telling you to look at, specifically this one:
inline fun CharSequence.replace(
regex: Regex,
noinline transform: (MatchResult) -> CharSequence
): String
Returns a new string obtained by replacing each substring of this char sequence that matches the given regular expression with the result of the given function transform that takes MatchResult and returns a string to be used as a replacement for that match.
So the lambda you provide is a function that takes a MatchResult
and does something with it, and returns a CharSequence (which can be a one-character long String). The replace function calls that lambda for every match that regex makes.
You get the general idea of what you're supposed to do? You have two parts here - the thing that identifies parts of the input string to process, and the thing that takes those matches and changes them into something else. The result is the original string with those changes made. So you need to come up with a regex and a transform that work together.
Nobody (probably) is going to tell you the answer because the point is figuring it out for yourself, but if you have any questions about things like regexes people will be happy to help you out! And speaking of, this site is extremely useful (I just used it myself to check I knew what I was doing): https://regex101.com/
Here is the implementation as pointed by #cactustictacs :
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
message.uppercase().replace(regex) { m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
and here is the another method almost same but with minor change using regex.replace()
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
regex.replace(message.uppercase()){m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
You can use it in place of m to make it slightly more concise.

How can I pass arguments to a Perl 6 grammar?

In Edit distance: Ignore start/end, I offered a Perl 6 solution to a fuzzy fuzzy matching problem. I had a grammar like this (although maybe I've improved it after Edit #3):
grammar NString {
regex n-chars { [<.ignore>* \w]**4 }
regex ignore { \s }
}
The literal 4 itself was the length of the target string in the example. But the next problem might be some other length. So how can I tell the grammar how long I want that match to be?
Although the docs don't show an example or using the $args parameter, I found one in S05-grammar/example.t in roast.
Specify the arguments in :args and give the regex an appropriate signature. Inside the regex, access the arguments in a code block:
grammar NString {
regex n-chars ($length) { [<.ignore>* \w]**{ $length } }
regex ignore { \s }
}
class NString::Actions {
method n-chars ($/) {
put "Found $/";
}
}
my $string = 'The quick, brown butterfly';
loop {
state $from = 0;
my $match = NString.subparse(
$string,
:rule('n-chars'),
:actions(NString::Actions),
:c($from++),
:args( \(5) )
);
last unless ?$match;
}
I'm still not sure about the rules for passing the arguments though. This doesn't work:
:args( 5 )
I get:
Too few positionals passed; expected 2 arguments but got 1
This works:
:args( 5, )
But that's enough thinking about this for one night.

Need regular expression that will work to find numeric and alpha characters in a string

Here's what I'm trying to do. A user can type in a search string, which can include '*' or '?' wildcard characters. I'm finding this works with regular strings but not with ones including numeric characters.
e.g:
414D512052524D2E535441524B2E4E45298B8751202AE908
1208
if I look for a section of that hex string, it returns false. If I look for "120" or "208" in the "1208" string it fails.
Right now, my regular expression pattern ends up looking like this when a user enters, say "w?f": '\bw.?f\b'
I'm (obviously) not well-versed in regular expressions at the moment, but would appreciate any pointers someone may have to handle numeric characters in the way I need to - thanks!
Code in question:
/**
*
* #param searchString
* #param strToBeSearched
* #return
*/
public boolean findString(String searchString, String strToBeSearched) {
Pattern pattern = Pattern.compile(wildcardToRegex(searchString));
return pattern.matcher(strToBeSearched).find();
}
private String wildcardToRegex(String wildcard){
StringBuffer s = new StringBuffer(wildcard.length());
s.append("\\b");
for (int i = 0, is = wildcard.length(); i < is; i++) {
char c = wildcard.charAt(i);
switch(c) {
case '*':
s.append(".*");
break;
case '?':
s.append(".?");
break;
default:
s.append(c);
break;
}
}
s.append("\\b");
return(s.toString());
}
Let's assume your string to search in is
1208
The search "term" the user enters is
120
The pattern then is
\b120\b
The \b (word boundary) meta-character matches beginning and end of "words".
In our example, this can't work because 120 != 1208
The pattern has to be
\b.*120.*\b
where .* means match a variable number of characters (including null).
Solution:
either add the .*s to your wildcardToRegex(...) method to make this functionality work out-of-the-box,
or tell your users to search for *120*, because your * wildcard character does exactly the same.
This is, in fact, my preference because the user can then define whether to search for entries starting with something (search for something*), including something (*something*), ending with something (*something), or exactly something (something).

NSExpression 1/2

I want to calculate a string, which I'm doing by this:
NSExpression *expression = [NSExpression expressionWithFormat:calculationString];
float result = [[expression expressionValueWithObject:nil context:nil] floatValue];
NSLog(#"%f", result);
The problem is, when calculationstring is 1/2, the result is 0. I tried to change float with double and NSNumber and the %f to %f and %#, but I always just get 0. What to I have to change?
Also if it matters, I am in Europe, so I have commas instead of points for this value, but it shouldn't matter as I am logging with %f which shows it as points. Just for information
Basically, you just need to tell it that you are performing floating point operation,
1.0/2
1.0/2.0
1/2.0
Will all work
Typing in NSExpression is much like in C: literals that look like integers (no decimal point/comma) are treated as integers and thus use integer division. (Under integer division, 1/2 is zero. If you want 0.5, you need floating point division.) This happens when the expression is parsed and evaluated, so attempting to change the type of the result or the formatting of the output has no effect -- those things happen after parsing and evaluation.
If your calculationString is entirely under your control, it's easy to make sure that you use floating point literals anywhere you want floating point division. (That is, use 1.0/2 instead of 1/2.) If not, you'll need to change it such that it does -- here it's probably better to decompose the parsed NSExpression and change an operand rather than munge the string.
Followup edit on the "decompose" bit: String munging in content that you know to have higher-order structure is generally problematic. And with NSExpression, you already have a parser (who's smarter than a simple regex) decomposing the string for you — that is in fact what NSExpression is all about.
So, if you're working with a user-provided string, don't try to change the expression by changing the string. Let NSExpression parse it, then use properties of the resulting object to pick it apart into its constituent expressions. If your string is simply "1/2", then your expression has an array of two arguments and the function "divide:by:" — you can replace it with an equivalent function where one of the arguments is explicitly a floating-point value:
extension NSExpression {
var floatifiedForDivisionIfNeeded: NSExpression {
if function == "divide:by:", let args = arguments, let last = args.last,
let firstValue = args.first?.constantValue as? NSNumber {
let newFirst = NSExpression(forConstantValue: firstValue.doubleValue)
return NSExpression(forFunction: function, arguments: [newFirst, last])
} else {
return self
}
}
}
I think You need to User DDMathParser Which is best in this situation. I have used it in One of my project which is facing same problem as you have faced
DDMathEvaluator *eval = [DDMathEvaluator defaultMathEvaluator];
id value=[eval evaluateString:#"1/2" withSubstitutions:nil error:&error];
NSLog(#"Result %#",value);
Result 0.5
Rickster's solution worked, but had problems with expressions like 5*5/2, where the first argument (here 5*5) was not just a number.
I found a different solution here that works for me: https://stackoverflow.com/a/46554342/6385925
for people who still have this problem i did a somewhat quick fix:
extension String {
var mathExpression: String {
var returnValue = ""
for value in newString.components(separatedBy: " ") {
if value.isOperator {
returnValue += value
} else {
returnValue += "\(Double(value) ?? 0)"
}
}
return returnValue
}
var isOperator: Bool {
["+", "-", "/", "x", "*"].contains(self)
}
}

Regular expression for separating words by uppercase letters and numbers

I was wondering if anyone might know what the regular expression would be to turn this:
West4thStreet
into this:
West 4th Street
I'm going to add the spaces to the string in Objective-C.
Thanks!
I don't know exactly where you want to put in spaces, but try something like [a-z.-][^a-z .-] and then put a space between the two characters in each match.
Something like this perl regex substitution would put a space before each group of capital letters or numbers. (You'd want to trim space before the string in this case also.) I assume you don't want it to break up eg: 45thStreet to 4 5th Street
Letters I'm less certain of.
s/([A-Z]+|[0-9]+)/ \1/g
I created a pattern to not match the beginning of the line for my personal amusement:
s/([^\^])([A-Z]+|[0-9]+)/\1 \2/g
This should work, if all your strings truly match the format of your example:
([A-Z][a-z]+)(\d+[a-z]+)([A-Z][a-z]+)
You can then separate the groups with spaces.
Another option would be to not use RegExKit and use code to loop through each character in the string and insert a space after each capital letter or after first decimal..
NSMutableString *myText2 = [[NSMutableString alloc] initWithString:#"The1stTest"];
bool isNumber=false;
for(int x=myText2.length-1;x>1;x--)
{
bool isUpperCase = [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[myText2 characterAtIndex:x]];
bool isLowerCase = [[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:[myText2 characterAtIndex:x]];
if([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[myText2 characterAtIndex:x]])
isNumber = true;
if((isUpperCase || isLowerCase) && isNumber)
{
[myText2 insertString:#" " atIndex:x+1];
isNumber=false;
}
if(isUpperCase)
[myText2 insertString:#" " atIndex:x];
}
NSLog(#"%#",myText2); // Output: "The 1st Test"