Universe Basic Dynamic Array Relative Operation - universe

Universe dynamic array relative operation (using -1) works in a strange way.
Operation below does not add new element in position <1,1,5> as I expected instead adds '1,1,5' to DYNAMIC.ARRAY<1,1,1>.
DYNAMIC.ARRAY = ' '
DYNAMIC.ARRAY<1,-1,5> = '1,1,5' ; *Adds to 1,1,1 not 1,1,5 when DYNAMIC.ARRAY contains only whitespaces before this operation
However same operation works as expected if dynamic array contains non empty value. Final result after executing code below will be DYNAMIC.ARRAY<1,1,1> = '1,1,1' and DYNAMIC.ARRAY<1,2,5> = '1,2,5'.
DYNAMIC.ARRAY = ' '
DYNAMIC.ARRAY<-1> = '1,1,1'
DYNAMIC.ARRAY<1,-1,5> = '1,2,5' ; *Adds to right position 1,2,5 when DYNAMIC.ARRAY is initialised to non empty value before this operation
Is this an expected behaviour in Universe?

When you use -1 it should be on the deepest level of nesting value.
The way multivalued fields work, what you want to do doesn't really make sense.
Say your record is is a reflection of things that customer bought, your dictionary might be something like
D1: CustomerName
D2: OrderNumber
D3: PartNumber
#ID 1234
0001:John Doe
0002:72832#VM83782#VM84783
0003:232-A#SVM2394-R#SVM3321-B#VM232-F#VM2342
CustomerName is a Single valued field. This is associated with entire record.
OrderNumber is a Value delimited list of Orders associated with at customer. In the SQL world this would be a child table.
PartNumber is a SubValue delimited list of Parts that is associated with each order. In the SQL world this would be a Child table of the Order Child Table.
Framing the logic like this, it really doesn't make any sense to say that you want to assign the 5th item on the next order the customer buys to be part "12345678" because you haven't got an order to associate with a part yet.
I believe there are some dictionary directives that you might be able to use to bypass this, but generally just know that it is bad form to create a sub-valued field without establishing an associated value first. When you start ignoring this you have to start validating for empty strings at ever turn. Down this road lies madness.
Hope that helps.
To summarize, you can't add a specific Sub Value to an unknown value. You have to first determine which value you want the subvalue mark to be in and then specify the subvalue.

From your code snippet
DYNAMIC.ARRAY = ' '
DYNAMIC.ARRAY<1,-1,5> = '1,1,5' ;* Adds to 1,1,1 not 1,1,5 when DYNAMIC.ARRAY contains only whitespaces before this operation
There are a number of things to be aware of
1) The white spaces have no bearing on what happens, as long as there are no reserved characters in the ASCII string (#FM,#AM,#VM,#SVM characters) the result will be the same.
2) The '-1' option should always be the last option and putting it in the second last parameter position will not work.
What you are trying to achive can be performed in many different ways
DYNAMIC.ARRAY<1,-1> = #SVM:#SVM:#SVM:#SVM:"1,1,5" ;* appends subvalued string as last value
or
DYNAMIC.ARRAY<1,-1> = STR(#SVM,4):"1,1,5" ;* appends subvalued string as last value
or
TEMP = "" ;* needs to be initialised
TEMP<1,1,5> = "1,1,5" ;* puts string in 5th subvalue position
DYNAMIC.ARRAY<1,-1> = TEMP ;* appends TEMP string as last value
or
VAL.POS = DCOUNT(DYNAMIC.ARRAY<1,1>,#VM) ;* find next value position
DYNAMIC.ARRAY<1,VAL.POS,5> = "1,1,5" ;* insert string into subvalue 5 of value

The '-1' use in inserting data into a dynamic array is a special notation, with its own rules. Using -1 essentially means "insert after last attribute, value or sub-value" (depending on where you have the -1 in your expression).
In your first example:
DYNAMIC.ARRAY = ' '
DYNAMIC.ARRAY<1,-1,5> = '1,1,5'
You are saying put the string '1,1,5' in the first attribute, AFTER THE LAST MULTIVALUE, as the 5th sub-value.
I would expect this to place the string '1,1,5' in position <1,2,5> because the -1 in the 'value' position says "put after last value" and because your initial array value was a single string of spaces, you already have something in array location <1,1,1> so the -1 causes a new value position to be added, and the 5 defines the subvalue position. So, result is a value placed into <1,2,5>
In your second example:
DYNAMIC.ARRAY = ' '
DYNAMIC.ARRAY<-1> = '1,1,1'
DYNAMIC.ARRAY<1,-1,5> = '1,2,5'
You start with the first line setting the array to a single attribute containing a string of spaces. The next line (with the <-1>) is saying "add a new attribute with the value '1,1,1'" which means you now have an array with 2 attributes. The third line (with the <1,-1,5>) means insert the string '1,2,5' in the first attribute, AFTER THE LAST VALUE, as the 5th sub-value, so I would again expect the result to be the string '1,2,5' to be in <1,2,5>
My comments are based on what I'd expect to see using R83 Pick, you do not say what version or 'account flavour' of UniVerse you are using, so perhaps that is part of the issue here.
It may be that the initial array of whitespace is being seen as an 'empty/null attribute' by UniVerse. I assume if you change the whitespace value in your first example to say 'ABC' then it all works as expected?

Related

Google Sheets: How can I extract partial text from a string based on a column of different options?

Goal: I have a bunch of keywords I'd like to categorise automatically based on topic parameters I set. Categories that match must be in the same column so the keyword data can be filtered.
e.g. If I have "Puppies" as a first topic, it shouldn't appear as a secondary or third topic otherwise the data cannot be filtered as needed.
Example Data: https://docs.google.com/spreadsheets/d/1TWYepApOtWDlwoTP8zkaflD7AoxD_LZ4PxssSpFlrWQ/edit?usp=sharing
Video: https://drive.google.com/file/d/11T5hhyestKRY4GpuwC7RF6tx-xQudNok/view?usp=sharing
Parameters Tab: I will add words in columns D-F that change based on the keyword data set and there will often be hundreds, if not thousands, of options for larger data sets.
Categories Tab: I'd like to have a formula or script that goes down the columns D-F in Parameters and fills in a corresponding value (in Categories! columns D-F respectively) based on partial match with column B or C (makes no difference to me if there's a delimiter like a space or not. Final data sheet should only have one of these columns though).
Things I've Tried:
I've tried a bunch of things. Nested IF formula with regexmatch works but seems clunky.
e.g. this formula in Categories! column D
=IF(REGEXMATCH($B2,LOWER(Parameters!$D$3)),Parameters!$D$3,IF(REGEXMATCH($B2,LOWER(Parameters!$D$4)),Parameters!$D$4,""))
I nested more statements changing out to the next cell in Parameters!D column (as in , manually adding $D$5, $D$6 etc) but this seems inefficient for a list thousands of words long. e.g. third topic will get very long once all dog breed types are added.
Any tips?
Functionality I haven't worked out:
if a string in Categories B or C contains more than one topic in the parameters I set out, is there a way I can have the first 2 to show instead of just the first one?
e.g. Cell A14 in Categories, how can I get a formula/automation to add both "Akita" & "German Shepherd" into the third topic? Concatenation with a CHAR(10) to add to new line is ideal format here. There will be other keywords that won't have both in there in which case these values will just show up individually.
Since this data set has a bunch of mixed breeds and all breeds are added as a third topic, it would be great to differentiate interest in mixes vs pure breeds without confusion.
Any ideas will be greatly appreciated! Also, I'm open to variations in layout and functionality of the spreadsheet in case you have a more creative solution. I just care about efficiently automating a tedious task!!
Try using custom function:
To create custom function:
1.Create or open a spreadsheet in Google Sheets.
2.Select the menu item Tools > Script editor.
3.Delete any code in the script editor and copy and paste the code below into the script editor.
4.At the top, click Save save.
To use custom function:
1.Click the cell where you want to use the function.
2.Type an equals sign (=) followed by the function name and any input value — for example, =DOUBLE(A1) — and press Enter.
3.The cell will momentarily display Loading..., then return the result.
Code:
function matchTopic(p, str) {
var params = p.flat(); //Convert 2d array into 1d
var buildRegex = params.map(i => '(' + i + ')').join('|'); //convert array into series of capturing groups. Example (Dog)|(Puppies)
var regex = new RegExp(buildRegex,"gi");
var results = str.match(regex);
if(results){
// The for loops below will convert the first character of each word to Uppercase
for(var i = 0 ; i < results.length ; i++){
var words = results[i].split(" ");
for (let j = 0; j < words.length; j++) {
words[j] = words[j][0].toUpperCase() + words[j].substr(1);
}
results[i] = words.join(" ");
}
return results.join(","); //return with comma separator
}else{
return ""; //return blank if result is null
}
}
Example Usage:
Parameters:
First Topic:
Second Topic:
Third Topic:
Reference:
Custom Functions
I've added a new sheet ("Erik Help") with separate formulas (highlighted in green currently) for each of your keyword columns. They are each essentially the same except for specific column references, so I'll include only the "First Topic" formula here:
=ArrayFormula({"First Topic";IF(A2:A="",,IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))) & IFERROR(CHAR(10)&REGEXEXTRACT(REGEXREPLACE(LOWER(B2:B&C2:C),IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))),""),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))))})
This formula first creates the header (which can be changed within the formula itself as you like).
The opening IF condition leaves any row in the results column blank if the corresponding cell in Column A of that row is also blank.
JOIN is used to form a concatenated string of all keywords separated by the pipe symbol, which REGEXEXTRACT interprets as OR.
IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))) will attempt to extract any of the keywords from each concatenated string in Columns B and C. If none is found, IFERROR will return null.
Then a second-round attempt is made:
& IFERROR(CHAR(10)&REGEXEXTRACT(REGEXREPLACE(LOWER(B2:B&C2:C),IFERROR(REGEXEXTRACT(LOWER(B2:B&C2:C),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>""))))),""),JOIN("|",LOWER(FILTER(Parameters!D3:D,Parameters!D3:D<>"")))))
Only this time, REGEXREPLACE is used to replace the results of the first round with null, thus eliminating them from being found in round two. This will cause any second listing from the JOIN clause to be found, if one exists. Otherwise, IFERROR again returns null for round two.
CHAR(10) is the new-line character.
I've written each of the three formulas to return up to two results for each keyword column. If that is not your intention for "First Topic" and "Second Topic" (i.e., if you only wanted a maximum of one result for each of those columns), just select and delete the entire round-two portion of the formula shown above from the formula in each of those columns.

How to convert text field with formatted currency to numeric field type in Postgres?

I have a table that has a text field which has formatted strings that represent money.
For example, it will have values like this, but also have "bad" invalid data as well
$5.55
$100050.44
over 10,000
$550
my money
570.00
I want to convert this to a numeric field but maintain the actual numbers that can be retained, and for any that can't , convert to null.
I was using this function originally which did convert clean numbers (numbers that didn't have any formatting). The issue was that it would not convert $5.55 as an example and set this to null.
CREATE OR REPLACE FUNCTION public.cast_text_to_numeric(
v_input text)
RETURNS numeric
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
declare v_output numeric default null;
begin
begin
v_output := v_input::numeric;
exception when others then return null;
end;
return v_output;
end;
$BODY$;
I then created a simple update statement which removes the all non digit characters, but keeps the period.
update public.numbertesting set field_1=regexp_replace(field_1,'[^\w.]','','g')
and if I run this statement, it correctly converts the text data to numeric and maintains the number:
alter table public.numbertesting
alter column field_1 type numeric
using field_1::numeric
But I need to use the function in order to properly discard any bad data and set those values to null.
Even after I run the clean up to set the text value to say 5.55
my "cast_text_to_numeric" function STILL sets this to null ? I don't understand why this sets it to null, but the above statement correctly converts it to a proper number.
How can I fix my cast_text_to_numeric function to properly convert values such as 5.55 , etc?
I'm ok with disgarding (setting to NULL) any values that don't end up with numbers and a period. The regular expression will strip out all other characters... and if there happens to be two numbers in the text field, with the script, they would be combined into one (spaces are removed) and I'm good with that.
In the example of data above, after conversion, the end result in numeric field would be:
5.55
100050.44
null
550
null
570.00
FYI, I am on Postgres 11 right now

PowerBI M: How to code in an exclusion to an IF statement

I'm working to programmatically clean up a field in my dataset by using a Helper column that I will later filter on and remove the 'junk' records. The junk records are ID's, and the valid records are full names (in the format of "Tom Jones"). Almost all (there is a valid name value of "University") junk records do not contain a space. The pseudo code would read
Set Helper_IsName? = True
WHERE ValueField CONTAINS " " unless ValueField = "University"
ELSE False
Here is the M code excerpt that is getting me 95% of the way there:
Helper_IsName? = Text.Contains([OldValue]," ")
All results are good, except when the formula reads "University", it sets the value as FALSE, when I need it to equal TRUE.
I think you can just add that condition with an or:
Helper_IsName? = Text.Contains([OldValue]," ") or [OldValue] = "University"

To append an element into a list without generating any other elements

Trying to append an element into an empty list as follows, while appending it also adds a character 'u' like [u'slice'] into the empty list while adding an element into the list, however expected is ['slice']
Code as follows:
type = slice # value for type
value = []
value.append(type)
Output:
value = [u'slice']
Requesting people for help to get output as ['slice'].
For some reason the output is in Unicode, hence the u. Try re-encoding it to ascii
by using .encode("ascii"). Sorry I don't know why it's doing that though.

Excel, duplicates in string, single cell iteration

I'm trying to extract certain pieces of data from a very long string within a single cell. For the sake of this exercise, this is the data I have in cell A1.
a:2:{s:15:"info_buyRequest";a:5:{s:4:"uenc";s:252:"WN0aW9uYWwuaHRlqdyZ2dC1hdD0lN0JhZHR5cGUlN0QmdnQtcHRpPSU3QmFkd29yZHNfcHJvZHVjdHRhcmdldGlkJTdEJiU3Qmlnbm9y,";s:7:"product";s:4:"1253";s:8:"form_key";s:16:"wyfg89N";s:7:"options";a:6:{i:10144;s:5:"73068";i:10145;s:5:"63085";i:10141;s:5:"73059";i:10143;s:5:"73064";i:13340;s:5:"99988";i:10142;s:5:"73063";}s:3:"qty";s:1:"1";}s:7:"options";a:6:{i:0;a:7:{s:5:"label";s:5:"Color";s:5:"value";s:11:"White";s:11:"print_value";s:11:"White";s:9:"option_id";s:5:"10144";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73068";s:11:"custom_view";b:0;}i:1;a:7:{s:5:"label";s:4:"Trim";s:5:"value";s:11:"Black";s:11:"print_value";s:11:"Black";s:9:"option_id";s:5:"10145";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"63085";s:11:"custom_view";b:0;}i:2;a:7:{s:5:"label";s:7:"Material";s:5:"value";s:15:"Vinyl";s:11:"print_value";s:15:"Vinyl";s:9:"option_id";s:5:"10141";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73059";s:11:"custom_view";b:0;}i:3;a:7:{s:5:"label";s:6:"Orientation";s:5:"value";s:17:"Left Side";s:11:"print_value";s:17:"Left Side";s:9:"option_id";s:5:"10143";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73064";s:11:"custom_view";b:0;}i:4;a:7:{s:5:"label";s:12:"Table";s:5:"value";s:16:"YES! Add Table";s:11:"print_value";s:16:"YES! Add Table";s:9:"option_id";s:5:"13340";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"99988";s:11:"custom_view";b:0;}i:5;a:7:{s:5:"label";s:8:"Shipping";s:5:"value";s:20:"Front Door Delivery";s:11:"print_value";s:20:"Front Door Delivery";s:9:"option_id";s:5:"10142";s:11:"option_type";s:9:"drop_down";s:12:"option_value";s:5:"73063";s:11:"custom_view";b:0;}}}
The end result, would be to separate the values for Color, Trim, Material Orientation, etc.
The formula I was using is this:
=MID(LEFT(A4,FIND("print_value",A4)-9),FIND("Color",A4)+25,LEN(A4))
This basically looks in between two points and trims out the fat. It works, but only for the first iteration of "print_value". If I were to use this searching for "Trim"...
=MID(LEFT(A4,FIND("print_value",A4)-9),FIND("Trim",A4)+25,LEN(A4))
...I get an empty result. This happens because print_value is duplicate and not unique to the string. Excel doesn't understand what point to apply its function to and poops itself.
Even though there are unique factors within this string that I could essentially attach myself to (and arrive at the desired result), I CAN NOT use them as they will not be consistent and will render the formula useless when applied to other cells.
That said, here is what I need. Within this formula, I need a way to either A) tell the formula which iteration of print_value to find or B) change print_value to print_value(1,2,3,4, etc) and then run my trimming formula.
Few options based on this link:
1) VBA - Using a User Defined Function
If you're new to these then follow this tutorial.
Function FindN(sFindWhat As String, _
sInputString As String, N As Integer) As Integer
Dim J As Integer
Application.Volatile
FindN = 0
For J = 1 To N
FindN = InStr(FindN + 1, sInputString, sFindWhat)
If FindN = 0 Then Exit For
Next
End Function
2) Using a Formula
=FIND(CHAR(1),SUBSTITUTE(A1,"c",CHAR(1),3))
c is the character you want to find
A1 is the text you want to look in
3 is the nth instance