I want to solve a conditional equation in iOS:
The equation I get from database is in NSString format, for example:
if((height > 0), (weight+ 2 ), ( weight-1 ) )
As per our understanding, if I parse the above string and separateheight>0condition, it will be in the NSString format. But to evaluate it how do I convert the string to a conditional statement?
Once the conditional statement is obtained the equation can be solved by converting it to a ternary equation as follows:
Bool status;
NSString *condition=#” height>0”;
If(condition) //But condition is treated as a string and not as a conditional statement.
{
status=True;
}
else
{
status=False;
}
Return status ? weight+ 2 : weight-1;`
Also the equations can dynamically change, so they cannot be hard coded. In short how do I solve this equation which I get as a NSString.
Thank you for your patience!
DDMathParser author here...
To expand on Jonathan's answer, here's how you could do it entirely in DDMathParser. However, to parse the string as-is, you'll need to do two things.
First, you'll need to create an if function:
DDMathEvaluator *evaluator = [DDMathEvaluator sharedMathEvaluator];
[evaluator registerFunction:^DDExpression *(NSArray *args, NSDictionary *vars, DDMathEvaluator *eval, NSError *__autoreleasing *error) {
if ([args count] == 3) {
DDExpression *condition = [args objectAtIndex:0];
DDExpression *resultExpression = nil;
NSNumber *conditionValue = [condition evaluateWithSubstitutions:vars evaluator:eval error:error];
if ([conditionValue boolValue] == YES) {
resultExpression = [args objectAtIndex:1];
} else {
resultExpression = [args objectAtIndex:2];
}
NSNumber *result = [resultExpression evaluateWithSubstitutions:vars evaluator:eval error:error];
return [DDExpression numberExpressionWithNumber:result];
}
return nil;
} forName:#"if"];
This creates the if() function, which takes three parameters. Depending on how the first parameter evaluates, it either evaluates to the result of the second or third parameter.
The other thing you'll need to do is tell the evaluator what height and weight mean. Since they don't start with a $ character, they get interpreted as functions, and not variables. If they started with a $, then it would be as simple as evaluating it like this:
NSString *expression = #"if(($height > 0), ($weight+ 2 ), ( $weight-1 ) )";
NSDictionary *variables = #{#"height" : #42, #"weight" : #13};
NSNumber *result = [expression evaluateWithSubstitutions:variables evaluator:evaluator error:nil];
However, since they don't start with a $, they're functions, which means you need to tell the evaluator what the functions evaluate to. You could do this by creating functions for both height and weight, just like you did for if:
[evaluator registerFunction:^DDExpression *(NSArray *args, NSDictionary *vars, DDMathEvaluator *eval, NSError **error) {
return [DDExpression numberExpressionWithNumber:#42];
} forName:#"height"];
Alternatively, you could make it a bit more dynamic and use the functionResolver block of DDMathEvaluator, which is a block that returns a block (woooooo) and would look like this:
NSDictionary *values = #{#"height": #42, #"weight": #13};
[evaluator setFunctionResolver:^DDMathFunction(NSString *name) {
DDMathFunction f = ^(NSArray *args, NSDictionary *vars, DDMathEvaluator *eval, NSError **error) {
NSNumber *n = [values objectForKey:name];
if (!n) { n = #0; }
return [DDExpression numberExpressionWithNumber:n];
};
return f;
}];
With those two pieces in place (registering if and providing the values of height and weight), you can do:
NSString *expression = #"if((height > 0), (weight+ 2 ), ( weight-1 ) )";
NSNumber *result = [expression evaluateWithSubstitutions:nil evaluator:evaluator error:nil];
... and get back the proper result of #15.
(I have plans to make DDMathParser allow unknown functions to fall back to provided variable values, but I haven't quite finished it yet)
you will have to write your own interpreter or find one that supports this kind of expressions.
The first part (the condition) can be evaluated by NSPredicate. For the second part (the calculation) you will need some math expression evaluation. Try this out https://github.com/davedelong/DDMathParser. Maybe you can do both with DDMathParser but i am not sure.
Related
I got a big issue when trying to parse json data in xcode. I have actually tried with two different parser and it still returns me a wrong json. Could anyone help in that ?
The string to parse (called jsonResp) is equal to :
{
"error":false,
"errorMessage":null,
"debugMessage":null,
"count":1,
"list":"links",
"data":[
{
"date":"Jeudi \u00e0 00:00:00",
"type":"friend",
"picture":"http://graph.facebook.com/22222222/picture? type=square",
"name":"Etouda Gaudo",
"ink_id":"1",
"chat_id":"1",
"count":"1",
"last_message":"CoUcou"
}
]
}
the string to parse is equal to :
NSData *jsonData = [jsonResp dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *dictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error];
NSLog(#"dictionary %#", dictionary);
and then I got the following result for the NSLog of dictionary :
dictionary {
count = 1;
data = (
{
"chat_id" = 1;
count = 1;
date = "Jeudi \U00e0 00:00:00";
"ink_id" = 1;
"last_message" = CoUcou;
name = "Test name";
picture = "http://graph.facebook.com/22222222/picture?type=square";
type = friend;
}
);
debugMessage = "<null>";
error = 0;
errorMessage = "<null>";
list = links;
}
I can't figure out why the " are missing...
Does anyone have a solution.
Thanks in advance.
NSLog is just a print representation for developers to view, it is the result of the description method being called on a class instance. Quotes are only added where the item might be ambitious without them such as a string with an embedded space. To verify that the JSON was parsed correctly validate it with code.
You are deserializing the JSON into an NSDictionary, which doesn't have to have quotes around it's property names, unlike JSON. Your parser is working correctly, but the NSLog of an NSDictionary won't show up exactly the same as the original JSON would.
I have a string that is being generate from a formula, however I only want to use the string as long as all of its characters are numeric, if not that I want to do something different for instance display an error message.
I have been having a look round but am finding it hard to find anything that works along the lines of what I am wanting to do. I have looked at NSScanner but I am not sure if its checking the whole string and then I am not actually sure how to check if these characters are numeric
- (void)isNumeric:(NSString *)code{
NSScanner *ns = [NSScanner scannerWithString:code];
if ( [ns scanFloat:NULL] ) //what can I use instead of NULL?
{
NSLog(#"INSIDE IF");
}
else {
NSLog(#"OUTSIDE IF");
}
}
So after a few more hours searching I have stumbled across an implementation that dose exactly what I am looking for.
so if you are looking to check if their are any alphanumeric characters in your NSString this works here
-(bool) isNumeric:(NSString*) hexText
{
NSNumberFormatter* numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
NSNumber* number = [numberFormatter numberFromString:hexText];
if (number != nil) {
NSLog(#"%# is numeric", hexText);
//do some stuff here
return true;
}
NSLog(#"%# is not numeric", hexText);
//or do some more stuff here
return false;
}
hope this helps.
Something like this would work:
#interface NSString (usefull_stuff)
- (BOOL) isAllDigits;
#end
#implementation NSString (usefull_stuff)
- (BOOL) isAllDigits
{
NSCharacterSet* nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
NSRange r = [self rangeOfCharacterFromSet: nonNumbers];
return r.location == NSNotFound && self.length > 0;
}
#end
then just use it like this:
NSString* hasOtherStuff = #"234 other stuff";
NSString* digitsOnly = #"123345999996665003030303030";
BOOL b1 = [hasOtherStuff isAllDigits];
BOOL b2 = [digitsOnly isAllDigits];
You don't have to wrap the functionality in a private category extension like this, but it sure makes it easy to reuse..
I like this solution better than the others since it wont ever overflow some int/float that is being scanned via NSScanner - the number of digits can be pretty much any length.
Consider NSString integerValue - it returns an NSInteger. However, it will accept some strings that are not entirely numeric and does not provide a mechanism to determine strings which are not numeric at all. This may or may not be acceptable.
For instance, " 13 " -> 13, "42foo" -> 42 and "helloworld" -> 0.
Happy coding.
Now, since the above was sort of a tangent to the question, see determine if string is numeric. Code taken from link, with comments added:
BOOL isNumeric(NSString *s)
{
NSScanner *sc = [NSScanner scannerWithString: s];
// We can pass NULL because we don't actually need the value to test
// for if the string is numeric. This is allowable.
if ( [sc scanFloat:NULL] )
{
// Ensure nothing left in scanner so that "42foo" is not accepted.
// ("42" would be consumed by scanFloat above leaving "foo".)
return [sc isAtEnd];
}
// Couldn't even scan a float :(
return NO;
}
The above works with just scanFloat -- e.g. no scanInt -- because the range of a float is much larger than that of an integer (even a 64-bit integer).
This function checks for "totally numeric" and will accept "42" and "0.13E2" but reject " 13 ", "42foo" and "helloworld".
It's very simple.
+ (BOOL)isStringNumeric:(NSString *)text
{
NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:text];
return [alphaNums isSupersetOfSet:inStringSet];
}
Like this:
- (void)isNumeric:(NSString *)code{
NSScanner *ns = [NSScanner scannerWithString:code];
float the_value;
if ( [ns scanFloat:&the_value] )
{
NSLog(#"INSIDE IF");
// do something with `the_value` if you like
}
else {
NSLog(#"OUTSIDE IF");
}
}
Faced same problem in Swift.
In Swift you should use this code, according TomSwift's answer:
func isAllDigits(str: String) -> Bool {
let nonNumbers = NSCharacterSet.decimalDigitCharacterSet()
if let range = str.rangeOfCharacterFromSet(nonNumbers) {
return true
}
else {
return false
}
}
P.S. Also you can use other NSCharacterSets or their combinations to check your string!
For simple numbers like "12234" or "231231.23123" the answer can be simple.
There is a transformation law for int numbers: when string with integer transforms to int (or long) number and then, again, transforms it back to another string these strings will be equal.
In Objective C it will looks like:
NSString *numStr=#"1234",*num2Str=nil;
num2Str=[NSString stringWithFormat:#"%lld",numStr.longlongValue];
if([numStr isEqualToString: num2Str]) NSLog(#"numStr is an integer number!");
By using this transformation law we can create solution
to detect double or long numbers:
NSString *numStr=#"12134.343"
NSArray *numList=[numStr componentsSeparatedByString:#"."];
if([[NSString stringWithFormat:#"%lld", numStr.longLongValue] isEqualToString:numStr]) NSLog(#"numStr is an integer number");
else
if( numList.count==2 &&
[[NSString stringWithFormat:#"%lld",((NSString*)numList[0]).longLongValue] isEqualToString:(NSString*)numList[0]] &&
[[NSString stringWithFormat:#"%lld",((NSString*)numList[1]).longLongValue] isEqualToString:(NSString*)numList[1]] )
NSLog(#"numStr is a double number");
else
NSLog(#"numStr is not a number");
I did not copy the code above from my work code so can be some mistakes, but I think the main point is clear.
Of course this solution doesn't work with numbers like "1E100", as well it doesn't take in account size of integer and fractional part. By using the law described above you can do whatever number detection you need.
C.Johns' answer is wrong. If you use a formatter, you risk apple changing their codebase at some point and having the formatter spit out a partial result. Tom's answer is wrong too. If you use the rangeOfCharacterFromSet method and check for NSNotFound, it'll register a true if the string contains even one number. Similarly, other answers in this thread suggest using the Integer value method. That is also wrong because it will register a true if even one integer is present in the string. The OP asked for an answer that ensures the entire string is numerical. Try this:
NSCharacterSet *searchSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
Tom was right about this part. That step gives you the non-numerical string characters. But then we do this:
NSString *trimmedString = [string stringByTrimmingCharactersInSet:searchSet];
return (string.length == trimmedString.length);
Tom's inverted character set can TRIM a string. So we can use that trim method to test if any non numerals exist in the string by comparing their lengths.
I have two arrays. One is an array of names and the other is an array made up of strings titled "Yes" or "No". The index path of each name in the "name" array corresponds with the same index path in the "Yes/No" array. For example:
Names Array | Yes/No Array
Person 1 | Yes
Person 2 | No
Person 3 | Yes
What would be the easiest way to look up a person's name (possibly getting the index path of it) and check whether they are "Yes" or "No" in the "Yes/No" array?
Also, I'm not sure if "index path" is the right term to use. If it isn't, I mean the number that an object is in an array.
NSArray has a method called indexOfObject that will return either the lowest index whose corresponding array value is equal to anObject or NSNotFound if no such object is found. If your array of names is unsorted, then use this to get the index that you can then plug in to the Yes/No array. That is, something along these lines:
NSString *answer = nil;
NSUInteger index = [namesArray indexOfObject:#"John Smith"];
if (index != NSNotFound) {
answer = [yesNoArray objectAtIndex:index];
}
return answer;
Because Bavarious asks questions where I assume, here's a better way when the array of names is sorted alphabetically.
int index = [self findName:#"John Smith"];
NSString *answer = nil;
if (index >= 0) {
answer = [yesNoArray objectAtIndex:index];
}
return answer;
where the function findName is a simple binary search:
-(int)findName:(NSString *)name {
int min, mid, max;
NSComparisonResult comparisonResult;
min = 0;
max = [namesArray count]-1;
while (min <= max) {
mid = min + (max-min)/2;
comparisonResult = [name compare:[namesArray objectAtIndex:mid]];
if (comparisonResult == NSOrderedSame) {
return mid;
} else if (comparisonResult == NSOrderedDescending) {
min = mid+1;
} else {
max = mid-1;
}
}
return -1;
}
Trying to keep two arrays synchronized is just asking for trouble. It can be done, of course, but whenever you modify one array, you have to remember to make a corresponding change to the other. Do yourself a favor and avoid that entire class of bugs by rethinking the way you're storing data.
In this case, you've got a {person, boolean} pair. One option is to store each pair as a dictionary, and then keep an array of those dictionaries. This would be a particularly good plan if you might expand the number of pieces of data beyond the two that you have. Another option would be to just use a dictionary where keys are person names and the values are your yes/no values. This makes the answer to your question very simple:
NSString *yesOrNo = [personDictionary objectForKey:personName];
Getting back to your original question, where you still have the two arrays, the easiest thing to do is to iterate over the person array until you find the person you're looking for, get the index of that name, and then look up the corresponding value in the yes/no array:
for (person in peopleArray) {
if ([person isEqualToString:thePersonYoureLookingFor]) {
yesNoValue = [yesNoArray objectAtIndex:[peopleArray indexOfObject:person];
break;
}
}
That's fine if the number of people in the list isn't too large. If the list could be large, then you'll want to keep the person array sorted so that you can do a binary search. The trouble there, though, is that you're yes/no array is separate, so sorting the personArray while keeping the yes/no array in the right order becomes complicated.
You can also use below of the code, May its useful to you,
NSSortDescriptor *_lastDescriptor = [[NSSortDescriptor alloc] initWithKey:#"" ascending:YES];
NSArray *_lastArray = [NSArray arrayWithObject:_lastDescriptor];
firstCharacterArray = (NSMutableArray *)[[nameIndexesDictionary allKeys]
sortedArrayUsingDescriptors:_lastArray];
//firstCharacterArray = (NSMutableArray *)[[nameIndexesDictionary allKeys]
sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
for (NSString *eachlastIndex in firstCharacterArray)
{
NSSortDescriptor *lastDescriptor = [[NSSortDescriptor alloc] initWithKey:#""
ascending:YES];
//selector:#selector(localizedCaseInsensitiveCompare:)] ;
NSArray *descriptorslast = [NSArray arrayWithObject:lastDescriptor];
[[nameIndexesDictionary objectForKey:eachlastIndex]
sortUsingDescriptors:descriptorslast];
[lastDescriptor release];
}
You can use indexOfObject method to get the index of element.
for example
This will give you index of your object
NSInteger index = [yourArray indexOfObject:objectName];
To see the corresponding element from another array
[anotherArray objectAtIndex:index];
This worked for me. Hope this helps.
I have an array that looks like this when printed via NSLog:
{
response = "Valid";
updates = (
{
string = "test";
integer = 3493;
},
{
string = "test2";
integer = 55454;
}
);
start-index = 0;
My question is how I can loop through through the "updates" array so that I may print the values for each "string" respectively.
Should be an easy "for" loop or something?
Dave
Assuming you NSLogged data has a type of NSDictionary with name data.
NSArray *updates = [data objectForKey:#"updates"];
for (NSDictionary *update in updates) {
NSLog(#"Update: %# - %#", [update objectForKey:#"string"], [update objectForKey:#"integer"]);
}
I got the problem that the if-statement doesn't work. After the first code line the variable contains the value "(null)", because the user who picked the contact from his iphone address book doesn't set the country key for this contact, so far so good.
but if I check the variable, it won't be true, but the value is certainly "(null)"... does someone have an idea?
NSString *country = [NSString [the dict objectForKey:(NSString *)kABPersonAddressCountryKey]];
if(country == #"(null)")
{
country = #"";
}
thanks in advance
sean
The correct expression would be:
if (country == nil)
which can be further shortened to:
if (!country)
And if you really want to test equality with the #"(null)" string, you should use the isEqual: method or isEqualToString:
if ([country isEqualToString:#"(null)"])
When you compare using the == operator, you are comparing object addresses, not their contents:
NSString *foo1 = [NSString stringWithString:#"foo"];
NSString *foo2 = [NSString stringWithString:#"foo"];
NSAssert(foo1 != foo2, #"The addresses are different.");
NSAssert([foo1 isEqual:foo2], #"But the contents are same.");
NSAssert([foo1 isEqualToString:foo2], #"True again, faster than isEqual:.");
That was a great answer I didn't know there was all those different ways of checking for nil or null string.