I'm trying to write a predicate to check a specific value in a dictionary I have set up.
This dictionary has string versions of "0", "1", and "2" as keys which I would like to access.
The predicate I would like to write is:
$player.currencyDictionaries.0.earned > 1000
The problem is that .0 is not allowed. Assuming I cannot easily change how the dictionary stores values (this is in older versions and I'd like to use the same predicate on all versions as it is hosted on a server) is there any way to access the data?
IIRC, you can do this:
$player.currencyDictionaries[0].earned > 1000
(You might need to do ['0'] to guarantee that the passed value is a string and not a number)
Note that this syntax ([0]) only works in predicate format strings. It does not work with key paths.
This syntax is defined under the "Index Expression" section of the Predicate BNF.
EDIT
Actually, this won't work, and here's why:
The string "$player.currencyDictionaries[0].earned" will be read and turned into an NSExpression of type NSKeyPathExpression. This means, when it's evaluated, it's going to basically take that string and run it through the receiver's -valueForKeyPath: method. As I mentioned above, the bracket syntax doesn't work with key paths, and thus this will produce the incorrect answer.
However, since you know the currencyDictionaries returns an NSDictionary, and since NSDictionary overrides the -valueForKey: method, you can turn the [0] bit into a key path, by turning it into a literal string:
$player.currencyDictionaries.'0'.earned
Related
According to the documentation, pg_attribute.attgenerated is typed as char and has a value of "a zero byte" if the column is not generated, and there is at least one other possible value, with potentially more in the future.
I want to query for all non-generated columns. Since I would prefer to not be tripped up by additions in future versions, the query predicate needs to be WHERE attgenerated = ZERO BYTE rather than an inequality, but I have no idea how to represent that value correctly in SQL.
What's the correct way to write this? In most programming languages you'd say '\0', and you can use escape sequences by prepending an e to the string literal, but if I say e'\0' it errors out with "invalid byte sequence for encoding "UTF8": 0x00". So I'm not quite sure what the right way to do this is.
It's simply an empty string:
WHERE attgenerated = ''
I have a column called "Bakery Activity" whose values are all JSONs that look like this:
{"flavors": [
{"d4js95-1cc5-4asn-asb48-1a781aa83": "chocolate"},
{"dc45n-jnsa9i-83ysg-81d4d7fae": "peanutButter"}],
"degreesToCook": 375,
"ingredients": {
"d4js95-1cc5-4asn-asb48-1a781aa83": [
"1nemw49-b9s88e-4750-bty0-bei8smr1eb",
"98h9nd8-3mo3-baef-2fe682n48d29"]
},
"numOfPiesBaked": 1,
"numberOfSlicesCreated": 6
}
I'm trying to extract the number of pies baked with a regex function in Tableau. Specifically, this one:
REGEXP_EXTRACT([Bakery Activity], '"numOfPiesBaked":"?([^\n,}]*)')
However, when I try to throw this calculated field into my text table, I get an error saying:
ERROR: function regexp_matches(jsonb, unknown) does not exist;
Error while executing the query
Worth noting is that my data source is PostgreSQL, which Tableau regex functions support; not all of my entries have numOfPiesBaked in them; when I run this in a simulator I get the correct extraction (actually, I get "numOfPiesBaked": 1" but removing the field name is a problem for another time).
What might be causing this error?
In short: Wrong data type, wrong function, wrong approach.
REGEXP_EXTRACT is obviously an abstraction layer of your client (Tableau), which is translated to regexp_matches() for Postgres. But that function expects text input. Since there is no assignment cast for jsonb -> text (for good reasons) you have to add an explicit cast to make it work, like:
SELECT regexp_matches("Bakery Activity"::text, '"numOfPiesBaked":"?([^\n,}]*)')
(The second argument can be an untyped string literal, Postgres function type resolution can defer the suitable data type text.)
Modern versions of Postgres also have regexp_match() returning a single row (unlike regexp_matches), which would seem like the better translation.
But regular expressions are the wrong approach to begin with.
Use the simple json/jsonb operator ->>:
SELECT "Bakery Activity"->>'numOfPiesBaked';
Returns '1' in your example.
If you know the value to be a valid integer, you can cast it right away:
SELECT ("Bakery Activity"->>'numOfPiesBaked')::int;
I found an easier way to handle JSONB data in Tableau.
Firstly, make a calculated field from the JSONB field and convert the field to a string by using str([FIELD_name]) command.
Then, on the calculated field, make another calculated field and use function:
REGEXP_EXTRACT([String_Field_Name], '"Key_to_be_extracted":"?([^\n,}]*)')
The required key-value pair will form the second caluculated field.
I have found that in lodash, the array methods also work on strings. For example:
> _.last('abc')
'c'
> _.indexOf('abc', 'x')
-1
Is this a standard behavior, and can this be relied on? The documentation does not say anything about it as far as I know.
Please note that the above methods are just examples. What I am more inclined to know is whether lodash expects its array methods to be used on strings. I need to write production code and I can not rely on something that works but the standard docs have not mentioned or acknowledged or guaranteed.
You can consider a String to essentially be an array of characters. They have certain properties and functions that you'd find on an Array, such as .length and .indexOf().
Based on the lodash source for .last and .indexOf, they use the .length property to determine the last character, or index of a character within an array.
These implementations, while could work with Strings in most scenarios because of their Array-like nature, will not work in all, since lodash uses bracket notation (str[0]) to find the last character/index of an item with an array. This is not universally supported e.g. for IE7, which is why the charAt method exists for accessing a character at a given index for a string.
You can do both of these things natively:
var str = 'mystring';
str.charAt(str.length - 1); // 'g'
var str = 'mystring';
str.indexOf('y') // 1`
Task:
I am planning to parse a formula string in NSPredicate and to replace variables in the string by their numeric values. The variables are names for properties of existing object instances in my data model, for instance I have a class "company" with an instance "Apple Corp."
Set-up:
My formula would like look like this: "Profitability_2011_in% = [Profit 2011] / [Revenue 2011]"
The instance "Apple Corp" would have the following properties:
Revenue 2009 = 10, Revenue 2010 = 20, Revenue 2011 = 30,
Profit 2009 = 5, Profit 2010 = 10, Profit 2011 = 20.
Hence, the formula would yield 20 / 30 = 67%.
Variables are usually two-dimensional, for instance defined by "profit" as the financial statement item and "year" (for instance 2011).
The variables are enclosed in [ ] and the dimensions are separated by " " (whitespace).
How I would do it
My implementation would begin with NSRegularExpression's matchesInString:options:range: to get an array of all variables in the formula (Profit 2011, Revenue 2011) and then construct an NSDictionary (key = variable name) out of this array by querying my data model.
What do you think?
Is there a better way to do it in your view?
In the formula, how would you replace the variables by their values?
How would you parse the formula?
Thank you!!
Yes, you can do this. This falls under the category of "Using NSPredicate for things for which it was not intended", but will work just fine.
You'll need to replace your variables with a single word that start with a $, since that's how NSPredicate denotes variables:
NSPredicate *p = [NSPredicate predicateWithFormat:#"foo = $bar"];
However you want to do that, great. NSRegularExpression is a fine way to do that.
Once you do that, you'll have something like this:
#"$profitability2011 = $profit2011 / $revenue2011"
You can then pop this through +predicateWithFormat:. You'll get back an NSComparisonPredicate. The -leftExpression will be of type NSVariableExpressionType, and the -rightExpression will be of type NSFunctionExpressionType.
This is where things start to get hairy. If you were to -evaluteWithObject:substitutionVariables:, you'd simply get back a YES or NO value, since a predicate is simply a statement that evaluates to true or false. I haven't explored how you could just evaluate one side (in this case, the -rightExpression), but it's possible that -[NSExpression expressionValueWithObject:context:] might help you. I don't know, because I'm not sure what that "context" parameter is for. It doesn't seem like it's a substitution dictionary, but I could be wrong.
So if that doesn't work (and I have no idea if it will or not), you could use my parser: DDMathParser. It has a parser, similar to NSPredicate's parser, but is specifically tuned for parsing and evaluating mathematical expressions. In your case, you'd do:
#import "DDMathParser.h"
NSString *s = #"$profit2011 / $revenue2011";
NSDictionary *values = ...; // the values of the variables
NSNumber *profitability = [s numberByEvaluatingStringWithSubstitutions:values];
The documentation for DDMathParser is quite extensive, and it can do quite a bit.
edit Dynamic variable resolution
I just pushed a change that allows DDMathParser to resolve functions dynamically. It's important to understand that a function is different from a variable. A function is evaluated, whereas a variable is simply substituted. However, the change only does dynamic resolution for functions, not variables. That's ok, because DDMathParser has this neat thing called argumentless functions.
An argumentless function is a function name that's not followed by an opening parenthesis. For convenience, it's inserted for you. This means that #"pi" is correctly parsed as #"pi()" (since the constant for π is implemented as a function).
In your case, you can do this:
Instead of regexing your string to make variables, simply use the names of the terms:
#"profit_2011 / revenue_2011";
This will be parsed as if you had entered:
#"divide(profit_2011(), revenue_2011())"
You can the set up your DDMathEvaluator object with a function resolver. There are two examples of this in the DDMathParser repository:
This example shows how to use the resolver function to look up the "missing" function in a substitution dictionary (this would be most like what you want)
This example shows you to interpret any missing function as if it evaluated to 42.
Once you implement a resolver function, you can forego having to package all your variables up into a dictionary.
Is there a better way to do it in your view?
Yes - using Flex & Bison.
Possibly you could achieve what you want with a regex - but for many expression grammars, a regex isn't powerful enough to parse the grammar. Also, regex things like this get large, unreadable, and unyieldy.
You can use Flex (a lexer) and Bison (a parser) to create a grammar definition for your expressions, and generate C code (which, as I'm sure you know, works perfectly with Objective-C since Objective-C is C) which you can use to parse your expressions.
In the formula, how would you replace the variables by their values?
As you parse through it with Bison you should have a hash table with variable names and their current values. When you generate the syntax tree, add references to the variables to your syntax tree nodes.
How would you parse the formula?
Again - Flex & Bison are specifically meant to do this kind of thing - and they excel at it.
I'm extracting terms from the query calling ExtractTerms() on the Query object that I get as the result of QueryParser.Parse(). I get a HashTable, but each item present as:
Key - term:term
Value - term:term
Why are the key and the value the same? And more why is term value duplicated and separated by colon?
Do highlighters only insert tags or to do anything else? I want not only to get text fragments but to highlight the source text (it's big enough). I try to get terms and by offsets to insert tags by hand. But I worry if this is the right solution.
I think the answer to this question may help.
It is because .Net 2.0 doesnt have an equivalent to java's HashSet. The conversion to .Net uses Hashtables with the same value in key/value. The colon you see is just the result of Term.ToString(), a Term is a fieldname + the term text, your field name is probably "term".
To highlight an entire document using the Highlighter contrib, use the NullFragmenter