INTERACT FORUM

More => Old Versions => JRiver Media Center 28 for Windows => Topic started by: Matt on March 17, 2020, 07:49:25 am

Title: Expression functions [HELP WANTED]
Post by: Matt on March 17, 2020, 07:49:25 am
Hi all,

I'm just spending a little time looking at the expression language and wondered if anyone has any ideas for functions they've wanted?

I just added a ListShuffle(...) function.  I would love to hear any other suggestions.

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: Moe on March 17, 2020, 09:39:51 am
I'd really love a mode function for the ItemCount function.  Something like mode 0, count all files that match the given values.  Mode 1, count all unique values that match the given values.

So, if I have a show that has two seasons, each season has 10 episodes

Code: [Select]
ItemCount(/[series/]/[season/],0)Would return 20

Code: [Select]
ItemCount(/[series/]/[season/],1)Would return 2
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on March 17, 2020, 11:20:11 am
We have:
round(x)    Returns x rounded to the nearest whole number.

Could we also/instead have:
round(x,n)    Returns x rounded to n decimal places.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 17, 2020, 11:48:21 am
We have:
round(x)    Returns x rounded to the nearest whole number.

Could we also/instead have:
round(x,n)    Returns x rounded to n decimal places.

Take a look at FormatNumber.

I think it already does this.  For example: FormatNumber(3.1415926, 2) outputs 3.14.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on March 17, 2020, 12:02:24 pm
Take a look at FormatNumber.

I think it already does this.  For example: FormatNumber(3.1415926, 2) outputs 3.14.
Ah ok, only been looking this from math() section. Thanks for fulfilling the request  8)
Title: Re: Expression functions [HELP WANTED]
Post by: ferday on March 17, 2020, 09:37:27 pm
a loop function   ;)

i really like the expression language and it's quite powerful as is, the only thing i would like (only semi-related) is to be able to refer to an album as an object, so we could make expressions over albums like average DR and junk
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 17, 2020, 10:54:21 pm
Thanks Matt for soliciting this feedback...

I have a couple of small suggestions, conveniences really...

First, to make the GroupSummary function easier to use:
Add an optional MODE to GroupSummary, ex: GroupSummary(Rating, 1)
Mode 0 would be the default if the option is omitted, and would function as GroupSummary does now.
When mode 1 is specified, GroupSummary would provide "clean" output.  For example, it would output "3.4" instead of "3.4 avg" or "12" instead of "12 total".  This would make it easier to integrate in expressions.

Second, although a lot of things, like the above, can be done with regex, it's just a reality that too many users find regular expressions too difficult or unapproachable.  So it would be nice if there were a function, perhaps called Number(), that performed the function of the following:
regex([String], /#(\d+\.?\d+?)#/, -1)

That would make things easier for a lot of people...
Title: Re: Expression functions [HELP WANTED]
Post by: marko on March 18, 2020, 12:47:34 am
The only thing I've bumped into on several occassions, is requiring "&DataType=[Date]" for sorting correctly when using the "FormatDate" functions.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on March 18, 2020, 03:54:53 am
Second, although a lot of things, like the above, can be done with regex, it's just a reality that too many users find regular expressions too difficult or unapproachable.  So it would be nice if there were a function, perhaps called Number(), that performed the function of the following:
regex([String], /#(\d+\.?\d+?)#/, -1)
+1 It is really tedious to do any math() in country where comma is a decimal point as MC only understand period. If I could just use something like Number([field]) instead of Replace([field],/,,.) it would be really helpful.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 07:28:50 am
First, to make the GroupSummary function easier to use:
Add an optional MODE to GroupSummary, ex: GroupSummary(Rating, 1)
Mode 0 would be the default if the option is omitted, and would function as GroupSummary does now.
When mode 1 is specified, GroupSummary would provide "clean" output.  For example, it would output "3.4" instead of "3.4 avg" or "12" instead of "12 total".  This would make it easier to integrate in expressions.

I can do that one.  Thanks!

Second, although a lot of things, like the above, can be done with regex, it's just a reality that too many users find regular expressions too difficult or unapproachable.  So it would be nice if there were a function, perhaps called Number(), that performed the function of the following:
regex([String], /#(\d+\.?\d+?)#/, -1)

What would the Number(...) function take and what would it output?  Is it just digging through a big string like "abc 34 def" for the 34?

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: Mike Foran on March 18, 2020, 07:43:53 am
Matt, I was just discussing in another thread how I would like a function that would act like search, but could be used as an expression. Ideally it would be two functions, but both working on the same principle.

[Anything] would check against a particular string and return a boolean result. So [Anything]="Bob Dylan" would return a 1 if ANY tag, custom or not (that has search keywords enabled), contained Bob Dylan. This would enable automatically populated and broad scoping playlists and views, from rules or expressions.

[Tags] would check against a particular string and return a list of any tag containing the string. So inputting "Kanye West" would return a list of the name of every tag, custom or not, that the name Kanye West appears in, so something like "Artist/ Producer/ Musician" This would enable users to more fully explore connections within their library, and might even be fed into Play Doctor to create more interesting automatic playlists.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 08:08:27 am
[Tags] would check against a particular string and return a list of any tag containing the string. So inputting "Kanye West" would return a list of the name of every tag, custom or not, that the name Kanye West appears in, so something like "Artist/ Producer/ Musician" This would enable users to more fully explore connections within their library, and might even be fed into Play Doctor to create more interesting automatic playlists.

I liked that one.  So next build:
NEW: Added a SearchTags(...) expression function that takes a value and returns all the fields that have a match.

Keep in mind that it's not a real fast function to search every field on all the files, so use it sparingly.

Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 08:32:15 am
Thought more about it and think I can add a parameter to SearchTags to make it only return true or false (your other idea).
Title: Re: Expression functions [HELP WANTED]
Post by: Mike Foran on March 18, 2020, 11:29:53 am
I'm not sure I thought through things properly. Ideally, the [Anything] (or SearchTags) would work as a rule for a view or playlist. So the rules would look something like:

[Media Type]=[Audio] [Anything]="Kanye West"

and that would populate the view or playlist with audio tracks that have Kanye West in *any* tag.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 11:39:13 am
You can use expression functions in the search language.  Check the wiki.
Title: Re: Expression functions [HELP WANTED]
Post by: Mike Foran on March 18, 2020, 11:51:54 am
I am unaware of a search expression that will search all tags with one command. The best I can figure, I would have to make a long list of specific search queries, one for each field (which is basically how I have been doing it). The problem with that approach is that I have to manually input each field I want included in the search, so if I introduce new fields, or forget a field, I have to go through and update all of the playlists that use that set of rules. This is a cumbersome situation. I'm hoping for a more dynamic solution that wouldn't need me to predict where a string might appear and build custom search parameters around it. I could just say "If this string is in any tag at all, include it here."
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 11:56:14 am
This search finds all my files with Jewel anywhere in them:
[=SearchTags(Jewel, 1)]=1
Title: Re: Expression functions [HELP WANTED]
Post by: Mike Foran on March 18, 2020, 12:03:59 pm
I'm confused. You just added this to the new build or this is something that is already in the program?
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 12:07:09 pm
I'm confused. You just added this to the new build or this is something that is already in the program?

Sorry, that's the function I added today based on your idea.  It will be in a coming build.  It seems to be working great.
Title: Re: Expression functions [HELP WANTED]
Post by: Mike Foran on March 18, 2020, 12:11:22 pm
AWESOME!!  ;D ;D ;D
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 18, 2020, 12:30:55 pm

What would the Number(...) function take and what would it output?  Is it just digging through a big string like "abc 34 def" for the 34?

Thanks.

Number() would dig through a string like "abc 34.1avg def" and return 34.1 including the decimal point.

It would be nice to accommodate users in other parts of the world if Number() respected the system-wide default radix character.  Some places use a . in decimal numbers while others use a comma.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 12:56:03 pm
Number() would dig through a string like "abc 34.1avg def" and return 34.1 including the decimal point.

It would be nice to accommodate users in other parts of the world if Number() respected the system-wide default radix character.  Some places use a . in decimal numbers while others use a comma.

Next build:
NEW: Added the expression function Number(...) that takes a string and finds the number (plus decimal) in the string.

Thanks for the idea!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 18, 2020, 01:45:04 pm
Awesome, Matt, thanks!

Did you do the GroupSummary one too?
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 01:46:58 pm
Awesome, Matt, thanks!

Did you do the GroupSummary one too?

Sure did.

Here's the history:
Changed: Added a second parameter to the GroupSummary(...) expression function that when set to one, does not output things like "avg" or "total".

This thread has yielded some really nice fruit.  Please keep the ideas coming!
Title: Re: Expression functions [HELP WANTED]
Post by: BigSpider on March 18, 2020, 05:42:55 pm
an instr([field],"character to find") would be a useful addition to locate a specified character in a string which could then be used with the left() right() functions.
Title: Re: Expression functions [HELP WANTED]
Post by: dtc on March 18, 2020, 06:00:08 pm
an instr([field],"character to find") would be a useful addition to locate a specified character in a string which could then be used with the left() right() functions.

+1

Should be expanded to find a sub-string in a string and also to start at a particular position in the string. Regex is just too complicated for many to do this simple search.

For example,

InStr([ start ], string1, string2, [ compare ])
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 18, 2020, 06:04:45 pm
Yeah, thanks Spider, I can't believe I forgot this.  We've needed that for a long time.

You can do a substring search with IsEqual() but it does not return the position of the string, it only returns a boolean.

So to modify what BigSpider said, what we really need is:

Instr(target, substring, start, mode) where:
 target is any string or field to be searched
 string is any arbitrary string or field we're looking for, not just a single character
 start is the optional 0-based starting position, defaulting to 0
 mode (optional) is 0 (the default) for case insensitive, 1 for case sensitive

And Instr returns the zero-based position of the substring in the target (to be compatible with Mid).

[Edit: I see dtc responded along similar lines while I was typing]
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 18, 2020, 06:10:29 pm
Good ideas.  Thanks 😊
Title: Re: Expression functions [HELP WANTED]
Post by: SkGe on March 18, 2020, 06:38:46 pm
Doing some cleaning in my lyrics i hit a problem.
Code: [Select]
=FixSpacing(RemoveCharacters(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace([lyrics], [Refrain], *), [Outro], *), [Chorus], *), [Bridge], *), [Verse 1], *), [Verse 2], *), [Verse 3], *), [Pre-Chorus], *), [Post-Chorus], *), *), 4)When using this type of expression, everything is clear on the lyrics but it adds additional empty line instead. Can we have something similar like notepad++ has with duplicate lines, where it just remove/add them.
RemoveLines(), wheres default remove lines is 0, 1 removes consecutive lines, 2 adds lines above or below, etc..
Title: Re: Expression functions [HELP WANTED]
Post by: BigSpider on March 18, 2020, 07:20:26 pm
Yeah, thanks Spider, I can't believe I forgot this.  We've needed that for a long time.

You can do a substring search with IsEqual() but it does not return the position of the string, it only returns a boolean.

So to modify what BigSpider said, what we really need is:

Instr(target, substring, start, mode) where:
 target is any string or field to be searched
 string is any arbitrary string or field we're looking for, not just a single character
 start is the optional 0-based starting position, defaulting to 0
 mode (optional) is 0 (the default) for case insensitive, 1 for case sensitive

And Instr returns the zero-based position of the substring in the target (to be compatible with Mid).

[Edit: I see dtc responded along similar lines while I was typing]
Yea, what Wer said, I was only asking for the basics but this would make it super useful.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on March 19, 2020, 04:15:31 am
Yeah, thanks Spider, I can't believe I forgot this.  We've needed that for a long time.

You can do a substring search with IsEqual() but it does not return the position of the string, it only returns a boolean.

So to modify what BigSpider said, what we really need is:

Instr(target, substring, start, mode) where:
 target is any string or field to be searched
 string is any arbitrary string or field we're looking for, not just a single character
 start is the optional 0-based starting position, defaulting to 0
 mode (optional) is 0 (the default) for case insensitive, 1 for case sensitive

And Instr returns the zero-based position of the substring in the target (to be compatible with Mid).

[Edit: I see dtc responded along similar lines while I was typing]
+1 This has been a headache plenty of times and yet I forgot it now when asked...
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 19, 2020, 06:55:09 am
I'd like a List processing function that would take each member and transform it in a given way. This would simplify many expressions:

transform(template, delimiter, list1, list2, ...)
- given a Template (expression) and one or more lists;
- for each element N of the first list, apply the given template using element N of each provided list;
- return list of transformed values

Example:
[Actors]=Daisy Ridley;Adam Driver;John Boyega;Oscar Isaac;Mark Hamill
[Roles]=Rey;Kylo Ren;Finn;Poe Dameron;Luke Skywalker

transform(<img src="tooltip:[L1]-[L2].png"> ,;,[Actors],[Roles])

output (list):
<img src="tooltip:Daisy Ridley-Rey.png"> ;<img src="tooltip:Adam Driver-Kylo Ren.png"> ;<img src="tooltip:John Boyega-Finn.png"> ;<img src="tooltip:Oscar Isaac-Poe Dameron.png"> ;<img src="tooltip:Mark Hamill-Luke Skywalker.png"> 

- For iteration N, [L1] refers to element N of list1 (Actors), [L2] refers to element N of list2 (Roles); in general [Lx] refers to element N of list X
- For simplicity when transforming just a single list, [L] can be the same as [L1]
- The current index N itself could be used as well with [N]
- it lists are of different size and [Lx] doesn't exist for a given N, its value is blank

This should be named ListSomething() for consistency - ListApply / ListBake / ListTransform / foreach ...
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 19, 2020, 07:01:34 am
Function to create a numbered List:
Range(start, step, count, delimiter=;)

Range(1,1,10)   => returns 1;2;3;4;5;6;7;8;9;10
Range(5,-1,6)   => returns 5;4;3;2;1;0

This could also be used with the above ListTransform() to create a sort of loop to generate a list:
transform(some expression,,Range(1,1,10))  => same as transform(some expression,,1;2;3;4;5;6;7;8;9;10) , returns a list of 10 elements where each element is Transformed.

Example:
transform(#[L1]: [L2],,Range(1,1,5), [Actors])

output (list):
#1: Actor1 Name;#2: Actor2 Name;#3: Actor3 Name;#4: Actor4 Name;#5: Actor5 Name
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 19, 2020, 07:05:50 am
Function to handle a List as a List of Pairs (sort of Dictionary<string,string>), and return the "value" of a given "key":

ListPair(pairList,key,default=,separator=;)
- pairList: list of paired values, like "key1;value1;key2;value2;key3;value3;..."
- key: key to search for - key search should only be done on even indexes (0, 2, 4 ...)
- default: default value returned if key is not found

returns: List value on the position immediately after where the Key is found.

Example:
[pairs]=DTS;DTSLabel;AAC;AACLabel;AC3;AC3Label;MP3;MP3Label
ListPair([pairs],AAC)                          => returns AACLabel
ListPair([pairs],someValueNotInList,Other)     => returns Other
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 19, 2020, 07:08:03 am
Last one :)

ListIndex(value, list, delimiter,mode)
Return 0-based index of value within list, -1 if not found.
mode=0 for case-insensitive (default), 1 for case-sensitive.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 19, 2020, 07:43:46 am
Bonus one ;D

chr(x) => insert char X, with Unicode support please. X is a number, or unicode codepoint (decimal or hex? up to you)
Alternatively, just adding a unicode codepoint directly to the expression could also work, like \u25CF/ directly in the text.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 19, 2020, 08:14:49 am
Thanks for all the ideas!

Next build will have these enhancements based on the feedback:
NEW: Added the expression function Find(...) to search for a substring in a string.
NEW: Added the expression function Trim(...) to trim non-printable characters and newlines from a string.
NEW: Added the expression function ListFind(...) to search a list for a value (takes a final parameter to decide if it should return the value or the index).
NEW: Added the expression function Range(...) to create a numbered list.

Cheers.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 19, 2020, 08:30:52 am
Thanks Matt.

Another example of how powerful the list Transform function could be:
transform(trim([L]),, [Keywords])      - trim all items in the [Keywords] list
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 19, 2020, 08:46:34 am
Quote
NEW: Added the expression function Trim(...) to trim non-printable characters and newlines from a string.

I like Trim() as it's standard naming, but I just want to point out that you could also add it as a new mode to Clean()
Clean([Director],4)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 19, 2020, 08:48:47 am
I like Trim() as it's standard naming, but I just want to point out that you could also add it as a new mode to Clean()
Clean([Director],4)


I thought about that, but the Clean(...) function thinks about articles and the like so I thought a new function would be a little neater.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 19, 2020, 12:24:42 pm
Matt,

I really want to say thank you for this thread.  Not just the work you're doing, but the thought behind it.

Asking for suggestions from users, and then actually implementing them, even if they're small things like this, goes a long way towards building good-will/restoring faith in JRiver.

Thank you!

-Will

Title: Re: Expression functions [HELP WANTED]
Post by: Mike Foran on March 19, 2020, 12:26:46 pm
Hear Hear!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 20, 2020, 08:27:23 am
Bonus one ;D

chr(x) => insert char X, with Unicode support please. X is a number, or unicode codepoint (decimal or hex? up to you)
Alternatively, just adding a unicode codepoint directly to the expression could also work, like \u25CF/ directly in the text.

Next build:
NEW: Added the Char(...) expression function to output a character from the numeric code of the character.

Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: dtc on March 20, 2020, 09:20:42 am
Next build:
NEW: Added the Char(...) expression function to output a character from the numeric code of the character.

Thanks!

Awesome!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 24, 2020, 08:57:27 am
Still hoping for more good ideas!  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: SkGe on March 26, 2020, 03:45:43 am
Right now we have fixspacing() which is used for some of fixing space between text. What if we have a nex expression like FixLines() where you can remove/add lines inside a text.
You can see a post i've made here about how to fix a possible solution. An ideea, hope so. Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 26, 2020, 01:17:05 pm
Right now we have fixspacing() which is used for some of fixing space between text. What if we have a nex expression like FixLines() where you can remove/add lines inside a text.
You can see a post i've made here about how to fix a possible solution. An ideea, hope so. Thanks.

I added the expression Trim(...) thinking it would remove leading and trailing stuff from the expression.

If you still need FixSpacing(...) can you explain in a little more detail how it would work?  Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: SkGe on March 26, 2020, 07:30:07 pm
As i add example FixSpacing() where i believe you know that it has 3 value where you can workaround
(1-convert camel case to spaced words; 2-convert double spaces to single spaces; 4-remove leading and trailing space)
What i want if it is possible to be added, a way to deal with double lines in a text (lyrics). Because when i'm doing some cleaning in lyrics, at the end of expression i have double lines which i can't find a way to remove them. So i come with an idea to add something similar to FixSpacing(), where the value for the next expression, ex. FixLines() is, add/remove lines.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 27, 2020, 08:03:03 am
Thanks for the information.

Next build will have this:
NEW: Added the expression function TrimLines(...) to standardize and consolidate newlines.

It takes a mode like this:
Mode (or together): 1 trims double newlines, 2: trims starting and ending newlines (optional: defaults to 3)

If there's anything more you're looking for from TrimLines(...), just let me know.  Thanks again.
Title: Re: Expression functions [HELP WANTED]
Post by: SkGe on March 27, 2020, 06:57:57 pm
Well thanks for this Matt! For now im waiting to see implemented.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 29, 2020, 11:46:32 am
Can't believe I missed this one:

ListJoin(list, joiner, separator)
list - list to join
joiner - string with witch to join the list elements
separator - list separator, optional

Example:
ListJoin(SomeList,\ - \ )  - returns a list with elements joined by " - "

This effectively replaces the list separator with something else. A replace() runs the risk of replacing commas/semicolons inside the list elements themselves.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 29, 2020, 11:47:25 am
Quick one: Is there already an easy way to add a \n (newline) ? I dislike how the literal newline on the expressions disrupt the readability.

EDIT: perhaps different whitespace handling modes could be introduced with a whitespace(expression, mode) function. Mode would be literal/explicit/legacy/etc.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 29, 2020, 12:31:32 pm
ListCombine() already exists.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 29, 2020, 01:03:29 pm
ListCombine() already exists.
Thanks, I didn't know you could specify the output delimiter on that one. It does the trick.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 29, 2020, 09:46:48 pm
I tried out the expressions in the new version and they work great, Matt.  Thanks!!

 ;D ;D ;D
Title: Re: Expression functions [HELP WANTED]
Post by: JimH on March 29, 2020, 10:02:05 pm
Thanks, everyone, for all the ideas and feedback.  Well done, Matt.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 05, 2020, 10:34:57 am
It is really tedious to do any Math() in country where comma is a decimal point as MC only understand period. If I could just use something like Number([field]) instead of Replace([field],/,,.) it would be really helpful.
Could this be added to Number()?
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 05, 2020, 05:10:45 pm
It looks like the Math expression function wasn't working with non-US decimals very well.  I'll try to shore it up in a coming build.  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: timwtheov on April 06, 2020, 06:18:18 pm
Just wanted to chime in and say please for these two related posts:

https://yabb.jriver.com/interact/index.php/topic,124543.msg862290.html#msg862290

https://yabb.jriver.com/interact/index.php/topic,124543.msg862439.html#msg862439

Either would probably have helped with my post here:

https://yabb.jriver.com/interact/index.php/topic,124790.0.html

Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 10, 2020, 05:19:03 am
Are these new functions documented somewhere? I at least couldn't find any.

One request would be reverse find, which would start from end and return first found index
Title: Re: Expression functions [HELP WANTED]
Post by: SkGe on April 12, 2020, 11:35:10 pm
Hello there. I want to know if it is possible to alter a bit the way length() works because now, it only can read characters and nothing more. Is a way to expand this to include also words in the expression?! Right now I'm using a site that can count words in a text, but it will be better to have this include in JRiver.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 13, 2020, 02:08:39 am
Hello there. I want to know if it is possible to alter a bit the way length() works because now, it only can read characters and nothing more. Is a way to expand this to include also words in the expression?! Right now I'm using a site that can count words in a text, but it will be better to have this include in JRiver.
Don't know how efficient it is but you could use ListCount(Field],/ ) to count words
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 13, 2020, 02:27:13 am
Good thinking lepa.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 13, 2020, 05:49:04 am
Matt, I have an idea...  But it's a bit harder than the others we've talked about here.

GroupSummary is a great function, but there's a way it could become incredibly more useful.

I propose a related function, let's call it GroupSummaryQuery.

Now currently GroupSummary summarizes a given field for the members of the current context.  If you're looking at an album, GroupSummary(Duration) summarizes the Duration for all the members of that album.

GroupSummaryQuery is related, but slightly abstracted.

GroupSummaryQuery(field1, field2) summarizes a given field2 not for members of the current context, but for files that share a field1 with the current context.


So for example, if you're looking at an individual track in the AC/DC album Back in Black, then:

GroupSummary(Duration) gives you the duration of just that track, because you're not looking at the album level.

GroupSummaryQuery(Album, Duration) would give you the total duration for all tracks in the Back in Black album.

GroupSummaryQuery(Artist, Duration) would give you the total duration of all AC/DC tracks.

The functionality can be extended if the first field can be combined like a string:
GroupSummaryQuery(Artist-Year, Rating) would give you the summarized rating of all the AC/DC tracks released the same year as the current track.

Performing GroupSummaryQuery(Artist, Duration) would report the exact same result if you were looking at ANY AC/DC track, because they all share the same Artist.

This would provide some neat facilities that are currently available only with global variables.  In a sense, it generates on-the-fly relational fields.  By eliminating some of the need for global variables, it would make many constructs much more approachable for people.

The exact same thing could also be done with GroupCount, creating GroupCountQuery.

What do you think?  It seems to me this could probably be easily built off the existing GroupSummary code.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 13, 2020, 08:36:30 am
Coming next build:
NEW: Added the expression function GroupSummaryQuery to build a summary for files matching the given fields.

Thanks for the suggestion!  Testing appreciated because it was a little complicated.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 13, 2020, 08:37:50 am
Coming next build:
NEW: Added the expression function GroupSummaryQuery to build a summary for files matching the given fields.

Thanks for the suggestion!  Testing appreciated because it was a little complicated.
Thanks
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 13, 2020, 12:28:48 pm
Cool!  Glad to test.  Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: mvandyke on April 14, 2020, 09:17:45 am
Thanks for the information.

Next build will have this:
NEW: Added the expression function TrimLines(...) to standardize and consolidate newlines.

It takes a mode like this:
Mode (or together): 1 trims double newlines, 2: trims starting and ending newlines (optional: defaults to 3)

If there's anything more you're looking for from TrimLines(...), just let me know.  Thanks again.

I have a Field AMG Artist Biography that has many extra lines between paragraphs, sometimes 4 or 5 lines (sometimes the lines contain spaces).  I would like to create an expression to trim the "blank lines" out and leave one or two blank lines between paragraphs.

Could you provide the syntax for this (it's currently not documented in the Expression Language area as it's new?

Thanks
Matt
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 14, 2020, 03:15:00 pm
Coming next build:
NEW: Added the expression function GroupSummaryQuery to build a summary for files matching the given fields.

Thanks for the suggestion!  Testing appreciated because it was a little complicated.
First tests going on. Could similar mode parameter as it is now in GroupSummary be added to this also to be able to automatically filter out postfix text please?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 14, 2020, 03:35:04 pm
Yep, I'd thought of that but thought he might have it in there by default...

Is GroupCountQuery in there too?  That's the other side of the coin.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 15, 2020, 02:55:31 am
Coming next build:
NEW: Added the expression function GroupSummaryQuery to build a summary for files matching the given fields.

Thanks for the suggestion!  Testing appreciated because it was a little complicated.
Using variables seems to be much more lightweight. GroupSummaryQuery really struggles on scrolling when used like this on biggish views

Code: [Select]
[ArtistAlbum] = [Album Artist (auto)][Album]
Code: [Select]
[=Save(0, v_AlbumDuration[ArtistAlbum])1]=1 [=Save(Math([Duration,0] + Load(v_AlbumDuration[ArtistAlbum])), v_AlbumDuration[ArtistAlbum])1]=1
Create expression column using
Code: [Select]
GroupSummaryQuery(ArtistAlbum, Duration)vs.
Code: [Select]
FormatDuration(Load(v_AlbumDuration[ArtistAlbum]))
Title: Re: Expression functions [HELP WANTED]
Post by: arcspin on April 15, 2020, 11:07:49 pm
Coming next build:

Hi Matt
I would like to bring to your attention zybex suggestions and try to find a way for them to be added into future builds.

zybex is doing an incredible job with his MCRatings software that are an tremendous help for me and others in making our Theater view the best it can be and zybex suggestions seems to be of importance in making McRatings even better.



Thank you and stay safe,

//arcspin
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 16, 2020, 07:49:25 am
I would like to bring to your attention zybex suggestions and try to find a way for them to be added into future builds.

I've implemented several of his ideas.

If you have a specific one I overlooked, please post it again.

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 16, 2020, 07:55:25 am
First tests going on. Could similar mode parameter as it is now in GroupSummary be added to this also to be able to automatically filter out postfix text please?

Next build:
Changed: The GroupSummaryQuery function accepts a parameter just like GroupSummary to turn off labeling.

Thanks for the suggestion.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 16, 2020, 08:50:53 am
I've implemented several of his ideas.
If you have a specific one I overlooked, please post it again.

Thanks Matt, appreciated.
I think he's referring to the transform() function I proposed above which would simplify many long expressions we're currently using.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 16, 2020, 08:55:46 am
Thanks Matt, appreciated.
I think he's referring to the transform() function I proposed above which would simplify many long expressions we're currently using.

Thanks.

Could you provide a few more examples of how Transform(...) would work?

I'm still trying to figure out if it would be possible in a straight forward way.

Thanks again.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 16, 2020, 10:45:04 am
Sure, thanks for taking a look!

Syntax: Transform(template, separator, List)

The goal is to transform each element of a list using a given pattern, returning a new list with the same size where each element is transformed.
For instance, this expression can be used to process the [Actors] field to get a series of <img> tags pointing to the Actor profile PNGs (to show Actor pics in theater/thumbnail view):
Code: [Select]
<img src="E:\JRiver\Actors\Left(listitem([Actors],0), 1)\listitem([Actors],0).png" width="130"> <img src="E:\JRiver\Actors\Left(listitem([Actors],1), 1)\listitem([Actors],1).png" width="130"> <img src="E:\JRiver\Actors\Left(listitem([Actors],2), 1)\listitem([Actors],2).png" width="130"> <img src="E:\JRiver\Actors\Left(listitem([Actors],3), 1)\listitem([Actors],3).png" width="130"> <img src="E:\JRiver\Actors\Left(listitem([Actors],4), 1)\listitem([Actors],4).png" width="130"> <img src="E:\JRiver\Actors\Left(listitem([Actors],5), 1)\listitem([Actors],5).png" width="130"> <img src="E:\JRiver\Actors\Left(listitem([Actors],6), 1)\listitem([Actors],6).png" width="130">That's 670 chars just to do that, and it assumes the list always has at least 6 elements.

The same output (better, since it handles any list size) would be accomplished with:
Code: [Select]
ListCombine(Transform(<img src="E:\JRiver\Actors\Left([L],1)\[L].png" width="130">,,[Actors]),,, )(Note: I'm using ListJoin() which does not exist, because I just realized that ListCombine() is meant to join 2 lists into 1 - I think there's nothing like ListJoin yet - to join list elements into a single string - and it would also be useful)

The idea is that each element of [Actors] gets inserted into the template in turn, replacing the placeholder [L]. If a list has 5 elements, this transform is done 5 times, returning a new list where each element is the "template" with the [L] replaced with the original element.

Sample implementation
Here's a naive implementation example in metacode (corner cases not handled):
Code: [Select]
Transform(template, separator, SourceList) {
    var transformed=new List<string>();
    foreach (string item in SourceList.Split(separator))
        string newItem = template.Replace("[L]", item)
        transformed.Add(newItem)
    return transformed.Join(separator);
}

Transforming multiple lists
I placed the List argument in last place because the function can be expanded to take any number of lists:

Transform(template, separator, List1, List2, List3...)

In this case, the template could include placeholders referencing each of the lists - I proposed [L1], [L2], [L3], etc. [L] could be a shorthand for [L1] when there's only one list. We could also have [N] for the current loop index (item position in list).

For instance, assume your have 3 lists:
Code: [Select]
[Tracks] = "Track1; Track2; Track3"
[Durations] = "3:45; 2:50; 4:27"
[Bitrates] = "224; 224; 160"

Code: [Select]
Transform("Song #[N]: [L1] ([L2] @ [L3] Kbps)",, [Tracks], [Durations], [Bitrates])
This would output a list of 3 elements:
Code: [Select]
Song #1: Track1 (3:45 @ 224 Kbps); Song #2: Track2 (2:50 @ 224 Kbps); Song #3: Track3 (4:27 @ 224 Kbps)
Sample implementation for multiple lists:
(needs bounds checks to handle cases where the number of elements on each list is different - just use "" if there's no element for a given index)
Code: [Select]
Transform(template, separator, list<list<string>> Lists) {
    var transformed=new List<string>();
    int max = HighestElementCount(Lists);
    for (int n = 0; n < max; n++) {
       string newItem = template.Replace("[N]", n);
       newItem = newItem.Replace("[L]", Lists[n][0]);
       for (int x = 0; x < Lists.Count(); x++)
           newItem = newItem.Replace("[L{x+1}]", Lists[n][x]);
       transformed.Add(newItem)
    }
    return transformed.Join(separator);
}

I used placeholders like [N], [L], [L1] etc because there are already similar placeholders in regex functions [R1], [R2], etc. Other placeholder format can be used, of course.

OK, I hope this is not too complex for implementation :)
Let me know if some point is not clear.

Zybex

EDIT: fixed 2nd algorithm
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 16, 2020, 10:53:23 am
ListCombine() already exists.

I just realized that ListCombine() is meant to join 2 lists into a new list, removing duplicate elements.
The ListJoin() I was proposing is meant to join the elements of a List and output a string (basically, replace the list separator with something else).

Code: [Select]
ListJoin(List, joiner, separator)
example:
Code: [Select]
ListJoin(1;2;3;4,/ -/ ,)output:  1 - 2 - 3 - 4
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 16, 2020, 12:28:46 pm
List combine can do what you're proposing Zybex.  I'm not understanding the difference.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 16, 2020, 12:34:29 pm
I'm playing with your transform idea.

I have this expression:
Transform(Song #[N]: [L1] /([L2] @ [L3] Kbps/), Track1; Track2; Track3, 3:45; 2:50; 4:27, 124; 224; 160)

And it outputs this:
Song #1: Track1 (3:45 @ 124 Kbps); Song #2: Track2 (2:50 @ 224 Kbps); Song #3: Track3 (4:27 @ 160 Kbps)

Do I have it working right?  Any other angles I need to consider?

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 16, 2020, 12:44:52 pm
Matt, that looks exactly right  ;D

If the template includes functions, does it work as well?
For instance (stupid example):

Transform(Song #[N]: [L1] /([L2] @ Math([L3]/8) KBps/), Track1; Track2; Track3, 3:45; 2:50; 4:27, 128; 224; 160)

should yield:
Song #1: Track1 (3:45 @ 16 KBps); Song #2: Track2 (2:50 @ 28 KBps); Song #3: Track3 (4:27 @ 20 KBps)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 16, 2020, 12:48:31 pm
List combine can do what you're proposing Zybex.  I'm not understanding the difference.

Wer,
True, ListCombine(1;2;3;4,,,/ -/ ) also achieves the same.
As MrC pointed out to me, even a simple Replace() can do what I mentioned since a list is, in practice, just a string.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 16, 2020, 01:13:28 pm
Matt, that looks exactly right  ;D

If the template includes functions, does it work as well?
For instance (stupid example):

Transform(Song #[N]: [L1] /([L2] @ Math([L3]/8) KBps/), Track1; Track2; Track3, 3:45; 2:50; 4:27, 128; 224; 160)

should yield:
Song #1: Track1 (3:45 @ 16 KBps); Song #2: Track2 (2:50 @ 28 KBps); Song #3: Track3 (4:27 @ 20 KBps)

You just love to make it hard on me!

But I'm trying.  If you put the string in a string literal escape, the Math(...) won't get translated as a function.

Then I can parse it in the transform function.

So it takes something like this:
Transform(/*Song #[N]: [L1] /([L2] @ Math([L3]+1) KBps)/*, Track1; Track2; Track3, 3:45; 2:50; 4:27, 128; 224; 160)

Not sure if there could be any way to make that automatic?

Cheers.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 16, 2020, 01:25:42 pm
Can't you delay the function eval for the template argument?

I suppose that when you are evaluating/executing some function, you first evaluate/execute each of the args:
Function(arg1, arg2, arg3)
=> Function(Evaluate(arg1), Evaluate(arg2), Evaluate(arg3))

What we need in this case is:
Evaluate(Transform(template, Evaluate(arg2), Evaluate(arg3), ...))

That is, the "template" is a special arg that is not evaluated before Transform(), but instead there's an extra call to Evaluate the final output.
Does this make sense to you? Is it possible?

2 other minor things:
- the second arg to Transform() should perhaps be the ListSeparator char, for consistency with other list funcs)
- if later on we think of new functionality, we would need a "mode" arg ... but since the Transform() takes a variable number of List args, then this "mode" would need to come before the Lists. Do you think it should be added now as a placeholder?

Thanks,
zybex
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 16, 2020, 01:27:52 pm
- the second arg to Transform() should perhaps be the ListSeparator char, for consistency with other list funcs)
- if later on we think of new functionality, we would need a "mode" arg ... but since the Transform() takes a variable number of List args, then this "mode" would need to come before the Lists. Do you think it should be added now as a placeholder?

I was thinking about going without specifying a delimiter.  I just think you should use semi-colon, or your life will be terrible with our expression engine!

If you can think of something to put in mode, I can add it.

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 16, 2020, 01:40:03 pm
I was thinking about going without specifying a delimiter.  I just think you should use semi-colon, or your life will be terrible with our expression engine!
That's fine.

Quote
If you can think of something to put in mode, I can add it.
Mode 0 - what we talked about
Mode 1 - ?? ??
Mode 2 - Profit!  ;D

OK, can't thing of anything right now.

One other detail: it's fine to reference the same [L{n}] multiple times in the template. they should all get processed/replaced.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 16, 2020, 02:03:09 pm
NEW: Added the expression function Find(...) to search for a substring in a string.
If I have understood correctly Find() behaves like find first.
Could there be a mode to return the last occurrence of the searched char/string Find([Text], searchedString, 2)

Scenario could be e.g. making own line feeds
Code: [Select]
[Text] = "I have many words and I want to cut this text after 20 characters max but want to keep the words as a whole"
So desired result would like this (cut before 20 character as it was in the middle of the word):
Code: [Select]
I have many words
and I want to cut this text after 20 characters max but want to keep the words as a whole

If(Compare(Length([Text]),>,20),Mid([Text], 0, Find(Mid([Text],0,20),/ , 2)), [Text])
2 flag indicates that mode is find last. So above expression would examine if input string is more than 20 char long and if it is, it would find last space before 20th character and output text only to that point.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 16, 2020, 06:17:48 pm
Can't you delay the function eval for the template argument?

I just figured out how to escape the string as part of the pre-process.  I think that will work.  I was trying to dig into the parsing but not having much luck.  Hopeful for release tomorrow.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 17, 2020, 07:51:42 am
Next build:
NEW: Added a Transform(...) expression function (see this thread for details: https://yabb.jriver.com/interact/index.php/topic,124543.0.html)
NEW: Added a mode to the Find(...) expression function "2" to reverse find instead of forward find in the string.

Thanks so much for the suggestions.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 17, 2020, 08:01:49 am
Yaaaay!

(https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/apple/237/party-popper_1f389.png)

Thanks Matt!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 17, 2020, 08:07:16 am
And just for the record, I did add a do-nothing flags field to Transform(...) thinking I better future proof!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 17, 2020, 08:12:46 am
Nice. I bet if you didn't we would find a need for it shortly after release... Murphy's law.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 17, 2020, 08:23:23 am
What about:
mode 0 - default as implemented
mode 1 - return as many elements as the shortest list
mode 2 - return as many elements as the first list

The code I proposed returns as many list elements as the longest list (using "" for the missing elements of other lists)
Mode 1 would stop when it reaches the end of one of the list arguments, preventing results with blanks.
Mode 2 would stop when it reaches the end of the first list

The code change would be:
Code: [Select]
int max = (mode == 2) ? Lists[0].Length : (mode == 1) ? ShortestElementCount(lists) : HighestElementCount(Lists);
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 17, 2020, 08:39:21 am
What about:
mode 0 - default as implemented
mode 1 - return as many elements as the shortest list
mode 2 - return as many elements as the first list

The code I proposed returns as many list elements as the longest list (using "" for the missing elements of other lists)
Mode 1 would stop when it reaches the end of one of the list arguments, preventing results with blanks.
Mode 2 would stop when it reaches the end of the first list

The code change would be:
Code: [Select]
int max = (mode == 2) ? Lists[0].Length : (mode == 1) ? ShortestElementCount(lists) : HighestElementCount(Lists);

You got it!

From the code:
Flags: flags (0: return # of longest list; 1: returns # of shortest list; 2: return # of first list)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 17, 2020, 08:47:35 am
Great :)
Please consider if it makes sense to switch mode 0 and 2. Should the default be # first list? Your call.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 22, 2020, 08:17:21 am
Just a thank you to zybex for all his help testing the new feature.  Next build will have a ListMix(...) expression.  It's built around the Transform(...) modeled above.

Thanks again everyone.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 26, 2020, 09:16:16 pm
Matt, here's a good one that would simplify things a lot with what we're doing with the custom tooltips/views.

Proposal:
IfCase, which functions as a switch or select case statement.


IfCase performs an IsEqual operation against one primary operand and several secondary operands, with an action for each secondary operand.  IfCase honors the same modes as IsEqual.  IfCase acts on the first match, and if no match occurs returns null.

IfCase(primary, mode, secondary1, action1, secondary2, action2, secondary3, action3..... etc)

Example:

IfCase([bitrate], 2, 64, Low, 128, Standard, 256, Medium, 320, High)
    Converts a numerical bitrate to a descriptive word.
   
IfCase(Length([Name]), 5, 50, Wow this is super long, 30, This is a very long track name, 20, This is not so long)
    Takes various actions based on the length of a field.

IfCase([Oscar Awarded to], 8, Matt, Matt wins on his first acting job, meryl, The Oscar went to Meryl again, carrey, Unbelievable result)
    Multiple actions based on the contents of one string

Something like this would save a lot of typing of nested Compare() or IsEqual() statements when matching things against a possible list of values, and makes for much more readable expressions.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on April 27, 2020, 06:59:55 am
+1 for wer's case. I've actually writing it sometimes like that and then realized that I'm missing conditionals for everyone else but the first one. Definitely would use this one.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 27, 2020, 08:17:07 am
Wer, coming next build:
NEW: Added the IfCase(...) expression function ( details here: https://yabb.jriver.com/interact/index.php/topic,124543.msg866100.html#msg866100 ).

Note that you'll need to reverse the order of your logic so it tests the longest string first, then shorter.  Because we just work down the list and find the first match.  So even really, really long strings are longer than long strings.

Thanks for the suggestion!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 27, 2020, 08:18:58 am
Just to expand, your above expression would need to be changed to be like this:
IfCase(Length([Name]), 5, 50, Wow this is super long, 30, This is a very long track name, 20, This is a long track name)
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 27, 2020, 12:42:50 pm
Yes, that's right, and that's what I intended.  I just wasn't paying much attention when I wrote the examples, I was trying to include things of different types.  I'll update the examples for posterity.

Thanks Matt!
Title: Re: Expression functions [HELP WANTED]
Post by: RD James on April 27, 2020, 08:44:33 pm
Would there be a way to make commands like IsEqual() operate on lists with ANY/ALL operators?
I find that I often end up writing needlessly complicated expressions because I have to use nested repeating commands.
Perhaps with a couple of new modes?

 9   List search ANY (case sensitive)
10   List search ANY (case insensitive)
11   List search ALL (case sensitive)
12   List search ALL (case insensitive)

For example, replacing:
Code: [Select]
If(IsEqual([Album Artist (auto)], various, 8), [Artist], If(IsEqual([Album Artist (auto)], multiple, 8), [Artist], [Album Artist (auto)]))

With:
Code: [Select]
If(IsEqual([Album Artist (auto)], various; multiple, 10), [Artist], [Album Artist (auto)])

There are other functions where this could be useful too.
I seem to remember having some expressions with a lot of needlessly complicated nested Replace() commands; e.g.
Code: [Select]
Replace(Replace(Replace([Album], :, ꞉), ?, ?), //,⧸)
(this is because MC replaces illegal characters with _ instead of good substitutes)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 09:44:47 am
Good idea!

Next build:
NEW: Added modes 9 (List search ANY (case sensitive)), 10 (List search ANY (case insensitive)), 11 (List search ALL (case sensitive)), and 12 (List search ALL (case insensitive)) to the IsEqual expression function.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 12:15:16 pm
For any and all, I don't see from the example how we're suppose to pass the multiple substrings.  Is it a list with a delimiter?  I don't get it.

Would it make more sense to make isequal or ifcase accept boolean operators on arguments?
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 12:19:26 pm
For any and all, I don't see from the example how we're suppose to pass the multiple substrings.  Is it a list with a delimiter?  I don't get it.

Would it make more sense to make isequal or ifcase accept boolean operators on arguments?

Here's a simple example:
IsEqual(a;b;c, a;b, 9) = 1
IsEqual(a;b;c, a;b, 11) = 0
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 12:48:43 pm
Those are a little too simple, because they already have equivalents:   
IsEqual(a;b;c, a;b, 9) == IsEqual(a;b;c, a;b, 7)
IsEqual(a;b;c, a;b, 11) == IsEqual(a;b;c, a;b, 0)

What do these do?

IsEqual(Moe/, Larry and Curly, Curly;Bob, 9)= ?
IsEqual(Moe/, Larry and Curly, Curly;Moe;Larry, 11)= ?


Also, Matt, don't forget if you're adding modes to IsEqual, they need to also be added to IfCase.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 01:01:36 pm
IsEqual(Moe/, Larry and Curly, Curly;Bob, 9)= ?

There's no Bob in the first string or Curly, so no match.  The match string is "Larry and Curly" which "Curly" isn't enough to match.

IsEqual(Moe/, Larry and Curly, Curly;Moe;Larry, 11)= ?

It's search all, and all don't match.  So zero again.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 01:11:39 pm
There's no Bob in the first string or Curly, so no match.  The match string is "Larry and Curly" which "Curly" isn't enough to match.

It was mode 9, ANY, so I would have expected a match if ANY of the second list was in the first.

Quote
It's search all, and all don't match.  So zero again.

All three elements in the 2nd list are present in the first. I would expect a match.

What you're describing is no different than what we already have.  What's an example where this actually does something new?
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 02:07:06 pm
What you're describing is no different than what we already have.  What's an example where this actually does something new?

A simple case that returns a match when I'm not aware of other functions that would:
IsEqual(a;b,b;a,11)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 29, 2020, 02:26:35 pm
It was mode 9, ANY, so I would have expected a match if ANY of the second list was in the first.
All three elements in the 2nd list are present in the first. I would expect a match.

I think you are misunderstanding the functionality. Here's a formal description:

ALL: Given a list A and a list B, IsEqual(A,B,9) returns TRUE if ALL elements of list B are present in list A, regardless of ordering of both lists
ANY: Given a list A and a list B, IsEqual(A,B,11) returns TRUE if AT LEAST ONE element of list B is present in list A

For the examples you gave:
Code: [Select]
IsEqual(Moe/, Larry and Curly, Curly;Bob, 9)= ?List A has only 1 element ("Moe/, Larry and Curly"), and list B has 2 elements which are both NOT in list A. So this returns FALSE. Keep in mind that the string must match in its entirety - "Moe/, Larry and Curly" is not equal to "Curly".  This function is the same as checking if an Intersect of the 2 lists is empy or not.

Code: [Select]
IsEqual(Moe/, Larry and Curly, Curly;Moe;Larry, 11)= ?List A is still only 1 element, and it's distinct from all 3 elements in the second list - so, ALL = false. This function is the same as checking if an Intersect of A and B returns all elements of B.

My own examples:
IsEqual(Moe;Larry and Curly;John, Jake;John;Bob, 9)    => true because John is in both lists
IsEqual(Moe;Larry and Curly;John, Jake;Curly;Bob, 9)   => false because no element of the second list is in the first list
IsEqual(Moe;Larry and Curly;John, John;Moe, 11)        => true because both John and Moe are in the first list
IsEqual(Moe;Larry and Curly;John, Curly;John;Moe, 11)  => false because Curly is not on first list (by itself)

Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 02:30:36 pm
Ok there's only one difference between my example

IsEqual(Moe/, Larry and Curly, Curly;Moe;Larry, 11)

and yours

IsEqual(a;b,b;a,11)

What I think you're saying is that what you're envisioning is something that only works on delimited lists, and only compares against whole elements of the lists.

I was thinking of something more flexible, where the list to be searched could be delimited or undelimited, and where the interpreter is performing AND or OR operations on the elements of the second list.

My interpretation answers the question "are any or all of the elements in the second list present in the first string"
Yours answers "are any or all of the elements in the second list also individual elements of the first list"

Mine would match on both of the examples, yours only matches when the first list is delimited.

The approach I was envisioning can perform the same function as yours:
IsEqual(Moe/, Larry and Curly, /;Curly/;, 11) = 0
but yours doesn't work against undelimited lists.

I suggest that the more flexible approach is better, because the user can user more or less  restrictive matching at will.

IsEqual([Actors], Meryl, 11) = 1  with yours won't work. You would have to have an exact match, like "Meryl Streep" or if roles are present "Meryl Streep [Joanna Kramer]".    Because the role name will vary, you'd have to have different version of this expression for every movie.  With mine, one search for "Meryl Streep" would work for any movie, but you could be more restrictive if you wanted.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 02:31:01 pm
IsEqual(Moe;Larry and Curly;John, John;Moe, 11)        => true because both John and Moe are in the first list

You're in search all mode here, so I'm returning a false.  It would only be true if you added "Larry and Curly".

Otherwise everything lined up.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 02:36:18 pm
ALL: Given a list A and a list B, IsEqual(A,B,9) returns TRUE if ALL elements of list B are present in list A, regardless of ordering of both lists
ANY: Given a list A and a list B, IsEqual(A,B,11) returns TRUE if AT LEAST ONE element of list B is present in list A

Except this formal description is not what Matt's example show.  What Matt is doing is shown below. Note the difference:
ALL: Given a list A and a list B, IsEqual(A,B,9) returns TRUE if EACH AND EVERY element of list B is present in list A as a whole element, regardless of ordering of both lists
ANY: Given a list A and a list B, IsEqual(A,B,11) returns TRUE if AT LEAST ONE element of list B is present in list A as a whole element

Lists are just strings, with special importance given to a delimiter character.

"Curly" is present in "Moe Larry and Curly", and it is also present in "Moe;Larry;Curly"

Your definition, and what I was thinking, would match against the first  and second.  Matt's ONLY matches against the second, because it is only looking at whole elements.  That is a crucial difference, and a limitation.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 29, 2020, 02:41:32 pm
You're in search all mode here, so I'm returning a false.  It would only be true if you added "Larry and Curly".

Otherwise everything lined up.

Hmm, ok. That's a pure Intersect then, only true if both lists have same content regardless or sort order. I think the variant I mentioned would also be useful.

And Wer's variants can also be useful... so, more modes? :)

EDIT: With so many variants, perhaps it would be better to put them in a separate ListEquals() function with different modes.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 02:44:49 pm
Hmm, ok. That's a pure Intersect then, only true if both lists have same content regardless or sort order. I think the variant I mentioned would also be useful.

And Wer's variants can also be useful... so, more modes? :)

EDIT: With so many variants, perhaps it would be better to put them in a separate ListEquals() function with different modes.

I like to make as little work for Matt as possible.

We don't need additional modes, it would just need to work like my suggestion.  Not because I'm being a jerk, but because my method can produce EITHER result, based on how you structure the expression.  The other way only works one way.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 29, 2020, 02:48:20 pm
No, the strict modes are needed, a simple "contains" is not always desired.
In some cases, you need exact matches to prevent "John" matching "Johnson"... (ie, a substring inadvertently matching another longer string)

edit: IsEqual(Moe/, Larry and Curly, /;Curly/;, 11) = 0
This is award and it implies the list needs to be manipulated, adding the ';' to each element before calling IsEqual.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 02:51:52 pm
The reason I'm digging into this is because right before this latest idea was posted, I was thinking about asking the question of would it be possible to support booleans in IsEqual and IfCase. We can insert math() for this but it's cumbersome.

In other words IsEqual(Dick and Jane went to town with Mary, Bob | Mary, 7) = 1

We could get a lot of mileage out of that.  If these new IsEqual modes were following the model I described, it would be basically the same thing, albeit with just AND and OR.  But matching against only whole elements is very restrictive.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 02:58:16 pm
No, the strict modes are needed, a simple "contains" is not always desired.
In some cases, you need exact matches to prevent "John" matching "Johnson"... (ie, a substring inadvertently matching another longer string)

edit: IsEqual(Moe/, Larry and Curly, /;Curly/;, 11) = 0
This is award and it implies the list needs to be manipulated, adding the ';' to each element before calling IsEqual.

That's just example syntax; it's irrelevant because MC knows how (or if) the first list is delimited.  There just needs to be some way to signal whole element. You could use ^Curly^ or whatever you want.

Perhaps the best solution would be to have some explicit "whole elements only" flag for the expression.  Text searches do this, with a "whole word only" option.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 03:04:39 pm
I just changed it to look at substring matching.  So "Larry" will now match "Larry and Curly".

I didn't add modes for it because it seemed a little overwhelming!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 03:09:36 pm
Yeah it can get complicated.  That's what I was wondering about the booleans.  Do you think booleans would be too complicated?

Also, did you add it to IfCase?  Anything added to IsEqual also has to be added to IfCase.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 03:24:52 pm
Yeah it can get complicated.  That's what I was wondering about the booleans.  Do you think booleans would be too complicated?

Also, did you add it to IfCase?  Anything added to IsEqual also has to be added to IfCase.

I made IfCase and IsEqual share all their compare functions and building of the rules.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 29, 2020, 03:31:23 pm
I just added more modes:
NEW: Added modes 13 through 16 to IsEqual to do full string matching (instead of partial).

Hopefully everybody is happy now (except me!).
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 03:55:23 pm
Thanks Matt.

Do you want the next idea to be really easy, or an even bigger challenge?  :P
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 29, 2020, 08:32:59 pm
Were you still going to do GroupCountQuery to go with GroupSummaryQuery?

It would enable us to do things like:

"You have math(GroupCountQuery(Director,Name,1)- 1) other films by [Director] in your library."
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on April 30, 2020, 08:43:44 am
Were you still going to do GroupCountQuery to go with GroupSummaryQuery?

It wasn't on my radar yet, but keep me on my toes!

Could you provide a little more information on the parameters and some examples?

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on April 30, 2020, 09:56:28 am
Matt, what about some additional HTML rendering tags, is that on the radar?
A few useful ones would be < hr>, < center>, < right>, and < Table> or some other equivalent way of creating columns and do horizontal alignment.

I suppose switching to a full HTML rendering engine is out of the question due to opening up too many ways to mess with it.
Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on April 30, 2020, 09:59:44 am
table is probably a step too far. We may be able to investigate horizontal alignment, and maybe < hr >, but no guarantees on horizontal alignment, as the layout engine does not necessarily always know the exact width of the window its designed to render onto.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 30, 2020, 01:22:58 pm
Understood about the difficulties of HTML rendering and tables.

One problem we face in theater view constantly is layout related, just getting things to fit.

What about as a starting point, just the ability to fit a string of text to one line?

<fit>This is quite a bit of text about [Director], shown here <img src="tooltip:image"> with an image.< br><//fit>

Anything within the fit tag would be shrunk uniformly as needed so it did not exceed the width of the current text line (no wrap).  If it's shorter than the line, no adjustment.  When fields and images are inserted inline, they have differing widths, and the line ending up too long so that it wraps really throws off formatting.

Is that doable?  It would help a lot. 
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 30, 2020, 03:06:58 pm
It wasn't on my radar yet, but keep me on my toes!

Could you provide a little more information on the parameters and some examples?

Thanks.

Certainly, I'd be glad to.

GroupCountQuery would perform the same function as GroupCount, but in the same abstracted manner as GroupSummaryQuery.

Now currently GroupCount counts the number of unique values of a field for the current context.   If you're looking at an album, GroupCount(Name) counts the number of different Names for all the members of that album.

GroupCountQuery is related, but slightly abstracted. Instead of counting what you're looking at, it counts things related to what you're looking at.

GroupCountQuery(field1, field2) counts a given field2 not for members of the current context, but for files that share a field1 with the current context.

So for example, if you're looking at an individual track in the AC/DC album Back in Black, then:

GroupCount(Track #) returns 1 because there is only 1 track # value in the context.  Likewise GroupCount(Name) would also return 1.

GroupCountQuery(Album, Track #) would give you the count of all tracks in the Back in Black album.
GroupCountQuery(Album, Name) would give you the count of all Names in the Back in Black album, probably the same result.

GroupCountQuery(Artist, Album) would give you the count of Albums by AC/DC.

The functionality can be extended if the first field can be combined like a string:
GroupCountQuery(Artist-Producer, Album) would give you the count of all AC/DC albums with the same producer as the current album.

Performing GroupCountQuery(Artist, Album) would report the exact same result if you were looking at ANY AC/DC track, because they all share the same Artist.

Thanks,

-Will


Title: Re: Expression functions [HELP WANTED]
Post by: seantrow on April 30, 2020, 10:23:03 pm
Is there a function to return the Nth value from a list (basically an indexer)?  This seems like a basic thing but I couldn't find one.

Thanks,
Sean
Title: Re: Expression functions [HELP WANTED]
Post by: wer on April 30, 2020, 10:35:10 pm
Sean,

Yes.  ListItem(list, position, delimiter)   Google "jriver expression language" for more.

But please start a new thread for future how-do-I questions.  This is not a thread for questions, we're developing new extensions to the expression language here.  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 01, 2020, 08:02:23 am
Coming next build:
NEW: Added a GroupCountQuery(...) expression function to group files by a field, then count another field.

I enter:
GroupCountQuery(Genre, Album)

And I get a count of the number of albums in each genre.  It's not the fastest expression, so use it sparingly.

Thanks for the suggestion.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on May 01, 2020, 12:41:00 pm
Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on May 03, 2020, 12:15:58 pm
ListRemove(list, value, mode) default mode 0=index, 1=value

Remove by index:
ListRemove(a;b;c,1,0) ==> a;c

Remove by value:
ListRemove(a;b;c,b,1) ==> a;c
Title: Re: Expression functions [HELP WANTED]
Post by: janpeeters on May 04, 2020, 07:30:58 am
I would love a Samplerate field without the 'Khz'.
I created a 'bitdepth | samplerate' column to save space. But the 'Khz' is superfluous for me and takes space ;-).

Sidenote:
Maybe there's a truncate function to remove x characters from the end of a field but that exceeds my knowledge.
If anyone knows I would love to hear it.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on May 04, 2020, 07:36:04 am
I would love a Samplerate field without the 'Khz'.
I created a 'bitdepth | samplerate' column to save space. But the 'Khz' is superfluous for me and takes space ;-).

Sidenote:
Maybe there's a truncate function to remove x characters from the end of a field but that exceeds my knowledge.
If anyone knows I would love to hear it.
You can trim " kHz" out by using RemoveRight (number 4 indicating how many characters to trim in below example)
Code: [Select]
RemoveRight([Sample Rate],4)
Title: Re: Expression functions [HELP WANTED]
Post by: janpeeters on May 04, 2020, 08:00:15 am
You can trim " kHz" out by using RemoveRight (number 4 indicating how many characters to trim in below example)
Code: [Select]
RemoveRight([Sample Rate],4)

Thanks for your swift help @lepa. Really appreciated.
Can I ask you one more thing. Is there also a function the round Samplerate in this example. I would rather see "16 | 44" then "16 | 44.1"

Thanks, Jan
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on May 04, 2020, 08:15:45 am
If Sample Rate is always normally displayed with one decimal then you'd just increase trim amount from 4 to 6
RemoveRight([Sample Rate],6)

remove 6 characters starting at right from "44.1 kHz" ==> 44.1 kHz
Title: Re: Expression functions [HELP WANTED]
Post by: janpeeters on May 04, 2020, 08:16:52 am
If Sample Rate is always normally displayed with one decimal then you'd just increase trim amount from 4 to 6
RemoveRight([Sample Rate],6)

Of course hahaha, You have a better brain for this then I have. Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 04, 2020, 02:49:29 pm
You can also use the new Number(...) function.  Here's an example:
FormatNumber(Number([In Sample Rate]), 0)
Title: Re: Expression functions [HELP WANTED]
Post by: janpeeters on May 04, 2020, 02:54:50 pm
You can also use the new Number(...) function.  Here's an example:
FormatNumber(Number([In Sample Rate]), 0)

Thanks Matt, is there a place where I can read upon this a bit more?
I'm sure that what you write will work but I don't understand a bit of it ;-) And I'd love to understand this better. 
Title: Re: Expression functions [HELP WANTED]
Post by: seantrow on May 06, 2020, 04:15:30 pm
This isn't quite a function expression language request, but one thing I would love is some kind of experimental function evaluator window to test/debug things.

Right now for instance, I might want to add a new computed field to my database.  I have to go through a pretty awkward loop of editing an expression in an external text editor (it might get pretty complicated), bringing up the library schema editor, paste in the new expression into the little text field there, then exit back out of all the dialogs and see what the result is.  (Usually, it's "oh xxx, I had a syntax error"...) It would be save a lot of time if I could change the expression and see the result in a single dialog.

(I realize that expressions can occur in various contexts, so this might be more complicated than it first seems.)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 06, 2020, 04:17:14 pm
This isn't quite a function expression language request, but one thing I would love is some kind of experimental function evaluator window to test/debug things.

Right now for instance, I might want to add a new computed field to my database.  I have to go through a pretty awkward loop of editing an expression in an external text editor (it might get pretty complicated), bringing up the library schema editor, paste in the new expression into the little text field there, then exit back out of all the dialogs and see what the result is.  (Usually, it's "oh xxx, I had a syntax error"...) It would be save a lot of time if I could change the expression and see the result in a single dialog.

(I realize that expressions can occur in various contexts, so this might be more complicated than it first seems.)

I just add an expression column and I think it works pretty well.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on May 06, 2020, 04:23:38 pm
That's what I do too.

It would be nice if the built in expression editors could link to use Notepad++ as an external editor so that syntax highlighting, autocomplete, and lexical hints could be used.  The built-in editor his hints and the "insert function" menu, but not much else.  So currently the process is just cut-and-paste back and forth.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on May 09, 2020, 02:13:25 am
RegexReplace(String, Search Regex, Replace Regex, mode)

https://yabb.jriver.com/interact/index.php/topic,120778.msg835176.html#msg835176
Title: Re: Expression functions [HELP WANTED]
Post by: wer on May 09, 2020, 03:45:18 am
Yes, that would be useful.  And since you're using the MS regex engine, there could be an option for overlapping matches.

What do you think of linking to NPP as an external editor? Could be with a hotkey, if you don't want to touch the GUI.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on May 15, 2020, 03:04:52 am
Can Lepa's RegexReplace function happen Matt, or is that a no go?
Title: Re: Expression functions [HELP WANTED]
Post by: Moe on May 15, 2020, 06:21:25 pm
RegexReplace(String, Search Regex, Replace Regex, mode)

https://yabb.jriver.com/interact/index.php/topic,120778.msg835176.html#msg835176

This would be incredible
Title: Re: Expression functions [HELP WANTED]
Post by: wer on May 15, 2020, 11:52:03 pm
Just mentioning here that I've found GroupCountQuery is not always returning correct results, and it's useless in the file info panel because it always returns 1 there.  I've reported this in the 26.0.80 release thread.   I'm hoping for a fix as this function is key to getting related information into the file info panel in Theater View.

To reiterate the issue here, in case people are not looking at the other thread:

I have 6 movies directed by John Houston...

If I use GroupCountQuery(Director,Name) in an expression column, it returns 6, which is correct.

If I use it the File Info Panel in Theater View, it always returns 1.  That's wrong; it should always return 6, regardless of where it's used.

For a bit more info, it seems to be returning the number of matches in the current view. (and the current view for the file info panel is always 1 file).  For example, if I use a Panes view and filter by Studio, the same function stops returning 6 and instead returns 2, which is the number of movies John Huston directed with the currently filtered Studio in the view.  That's not correct.  It should only return 2 if the expression was GroupCountQuery(Director;Studio, Name).

GroupCountQuery(Director,Name) should return the same result globally, no matter which of that Director's movies you look at and no matter the view.  That's what we discussed.  A function that always returns 1 in the file info panel has no utility at all.

GroupSummaryQuery has the same problem: it seems to be context sensitive. That means it can't be used in Theater View either.

It also seems upon further testing that there is no ability with GroupSummaryQuery to join fields in the search, as GroupCountQuery has.

GroupSummaryQuery(Artist,Duration) should return the total duration of all tracks by that artist. It does in an expression column, but not in theater view.

GroupSummaryQuery(Artist;Composer,Duration) should return the total duration of all tracks with the same artist and composer. But this functionality is missing.

I regret not being able to thoroughly test these earlier, but some of this wasn't pushed out to the regular in-app-update channel until after a multi-week delay, and then the last couple of weeks I have been preoccupied with the Syntax Highlighter. So this is my first attempt to do anything serious with them.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 18, 2020, 09:36:36 am
GroupCountQuery was working on the working set of files, but there is no working set in the Theater View info panel.

So I'll try to make the next build just use all the files in the library.

I'm making the change to GroupCount and GroupCountQuery.

Testing appreciated once the build ships.  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on May 18, 2020, 02:46:51 pm
Thanks.  Lepa and I were talking about this, as he was having issues with slowness of these functions.

I thought it odd, as these functions perform a search essentially the same as filtering in a panes view, and filtering in a panes view is extremely rapid.

Are these Group...Query functions using a totally different search methodology?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on May 20, 2020, 05:45:13 pm
Matt, there's a lot that could be done to make Regex() more usable, but for starters, could it be changed to either return all non-overlapping matches to a single capture group (like Boost normally would) populating R1..R9 as appropriate, or at least, using the existing multi-explicit-capture-group syntax, not throw away the first 5 matches if match 6 isn't there, because the string was too short? Regex matching against lists of variable length is problematic.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 21, 2020, 11:33:55 am
I'll make the next build have this:
NEW: The Regex expression function takes a -2 mode that outputs all the captures as a semi-colon delimited list.

Thanks for the idea.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on May 31, 2020, 01:19:41 am
I'll make the next build have this:
NEW: The Regex expression function takes a -2 mode that outputs all the captures as a semi-colon delimited list.

Thanks for the idea.
I thought at first that this would allow for producing a list, from a list, like so...

(I really can't wrap my head around regex, so forgive the laborious explanation coming...)

If the source list contains, amongst other things:

A/1;A/2;A/3;A/4

MrC kindly provided regex to search for "A anything ;" and then remove the "A/". This much I can just about keep up with.
The result, is 1, because the regex stops as soon as it finds a match.
I want, "1;2;3;4"

When I first read this change, I thought this would do it, but, I can't get there. Is this because I misunderstood and it's still not possible, or because I'm not constructing correctly?

I initially thought that by changing the mode here to -2, it would place a string of all matches into [R1], but, I now think that what this actually does is return a list from [R1];[R2];[R3] etc.?
As my expression only ever returns an [R1], my goal is still unattainable, right?
Code: [Select]
if(regex([keywords],/#(!People[^;]+)#/,0),replace([R1],!People\,),)&datatype=[list]
I sat down ages ago to add this to the wiki, and that still hasn't happened yet! Reminds of when we used to send our kids to tidy their rooms.... Never happened :D
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on May 31, 2020, 03:43:24 am
Marko,
I'm not sure I understand what you're trying to do, but I can see several ways of doing what I think you want.
the following expressions assume you want to take out the prefix "!People\" from list elements:

1. Why not just do replace([Keywords],!People\,) ?

2. You can use ListMix() to apply a function to each element of the list:
ListMix(/#Replace([L1],!People\,)/#, 0, [Keywords])
ListMix(/#Regex([L1],!People\(.+), 1)/#, 0, [Keywords])

3. Regex with mode -2:
Regex([keywords],/#!People\([^;]+)#/,-2)
Title: Re: Expression functions [HELP WANTED]
Post by: marko on May 31, 2020, 05:57:20 am
Thanks for replying... Here's exactly what I want...

Take this string, a [Keywords] example...
Code: [Select]
!Events\Annual Christmas Get-Together;!People\Family\MarkW;!People\Family\Nettie;!People\Friends\Alex;!People\Friends\David;!People\Friends\John Stevens;!People\Friends\Louise Stevens;!People\Friends\Megan;!People\Friends\Tracy C;!Places\!Edinburgh\Platform 5
Extract all instances of !People\...\...;
Remove !People\ from each instance
return new list: Family\MarkW;Family\Nettie;Friends\Alex;Friends\David;Friends\John Stevens;Friends\Louise Stevens;Friends\Megan;Friends\Tracy C

Is that possible?
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on May 31, 2020, 06:04:16 am
Thanks for replying... Here's exactly what I want...

Take this string, a [Keywords] example...
Code: [Select]
!Events\Annual Christmas Get-Together;!People\Family\MarkW;!People\Family\Nettie;!People\Friends\Alex;!People\Friends\David;!People\Friends\John Stevens;!People\Friends\Louise Stevens;!People\Friends\Megan;!People\Friends\Tracy C;!Places\!Edinburgh\Platform 5
Extract all instances of !People\...\...;
Remove !People\ from each instance
return new list: Family\MarkW;Family\Nettie;Friends\Alex;Friends\David;Friends\John Stevens;Friends\Louise Stevens;Friends\Megan;Friends\Tracy C

Is that possible?
I thought this was exactly the point of -2 although havent test it yet myself.
wer gave here some regex example how to catch all the instances between similar patterns. Of course at that time there wasn't -2 option yet so it didn't work then.
https://yabb.jriver.com/interact/index.php/topic,119385.msg867397.html#msg867397
Title: Re: Expression functions [HELP WANTED]
Post by: marko on May 31, 2020, 07:05:46 am
Quote
I thought this was exactly the point of -2 although havent test it yet myself.
Me too, untill I got up close and personal with it...

I want to capture Everything between !People and ;, and then remove all instances of !People\
My issue being, the regex function stops after it has captured the first instance. I want it to carry on, finding each instance, placing each into [R1] to [Rn], then, I believe, the -2 mode could output my list. As it is with what I have at the moment, I only ever get as far as [R1], so have to fall back on 20 nested if(isequal( functions...
Code: [Select]
listbuild(1,;,if(isequal(listitem([keywords],0,;),people,8),replace(listitem([keywords],0,;),!People\,),),if(isequal(listitem([keywords],1,;),people,8),replace(listitem([keywords],1,;),!People\,),), and so on...20 being an arbitrary number that ensures all people are always captured. I think this is having a pretty bad performance affect in theater view, where on-screen responses to navigation from the remote in my image library take a good three seconds a press.

I was hoping regex could provide a more streamlined and efficient solution, but think that the answer is still, no, hoping someone can say, actually, yes, try this... :)

-marko
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on May 31, 2020, 08:59:25 am
Regex mode -2 should work for that, but I just tested it and it looks like -2 is not yet available/working.
I made that work with this:

Code: [Select]
Replace(ListMix(/#if(Compare(find([L1],!People\),>=,0),Replace([L1],!People\,),)#/, 0, [keywords]),; ;,;)
- Uses ListMix to evaluate each item in the list [keywords]
- for each item, if it contains "!People\", replace the "!People\" with nothing and return the rest; else return blank;
- cleanup the resultng list with Replace() to remove empty list items (I thought there was some other function to do this...)

Title: Re: Expression functions [HELP WANTED]
Post by: lepa on May 31, 2020, 10:39:51 am
Code: [Select]
Replace(ListMix(/#if(Compare(find([L1],!People\),>=,0),Replace([L1],!People\,),)#/, 0, [keywords]),; ;,;)
Hah, that's clever  :) I also thought there was a function to exclude empty items but I couldn't find any. ListClean() could be a good canditate for that...
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on May 31, 2020, 12:18:39 pm
One new function proposal that would help here:

ListGrep(list, text, mode)

Returns all elements of list which contain text.
Mode 0 - case insensitive
Mode 1 - case sensitive

ListGrep(redneck;blue;red roses, red) = "redneck; red roses"

Then the above code could be reduced to
Code: [Select]
Replace(ListGrep([Keywords], !People\), !People\,)
Title: Re: Expression functions [HELP WANTED]
Post by: marko on May 31, 2020, 12:55:26 pm
Looks interesting, thanks zybex. Might be a day or three before I can play and feedback, back to work in the morning.

ListGrep would be great too 👍
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on May 31, 2020, 06:22:54 pm
Regex mode -2 should work for that, but I just tested it and it looks like -2 is not yet available/working.

The new mode isn't available until MC26.0.84, which isn't public yet. However, Marko has been working with a version that includes the mode.

It would be great if someone could test and provide examples for the use of Mode -2 when a Public Release comes out that includes it. Then we can update the Wiki.
Title: Re: Expression functions [HELP WANTED]
Post by: JimH on May 31, 2020, 06:37:23 pm
zybex, you have mail.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on June 01, 2020, 12:51:05 pm
Had a very quick play with this, and, it works... with a few 'buts'...

I had to wrap your expression in two expressions... ListClean, removing duplicates still left some trailing semi colons, so, I tested for that, removing if necessary, resulting in quite a cumbersom expression, that nonetheless, worked...
Code: [Select]
if(isequal(Right(ListClean(Replace(ListMix(/#if(Compare(find([L1],!People\),>=,0),Replace([L1],!People\,),)#/, 0, [keywords]),; ;,;),1),1),/;),removeright(ListClean(Replace(ListMix(/#if(Compare(find([L1],!People\),>=,0),Replace([L1],!People\,),)#/, 0, [keywords]),; ;,;),1),1),ListClean(Replace(ListMix(/#if(Compare(find([L1],!People\),>=,0),Replace([L1],!People\,),)#/, 0, [keywords]),; ;,;),1))
I think there's a bug in the the ListMix function because, if there are brackets in the list items, a syntax error is produced, only for that item, the rest are processed correctly. For example...
After running through the expression above, this keywords string:
Code: [Select]
!Events\!Concerts\Take That (Caroline & Vada); !People\Family\Caroline; !People\Family\Vada; !Places\!Glasgow\The Hydro
Returns:
Code: [Select]
Expression Error (Syntax Error); Family\Caroline; Family\Vada
I won't be able to test this against Theater View performance on the HTPC until at least Sunday. Thanks for your help with this zybex.

Do you really think the regex "-2" mode will catch these? How would you get it to to place all the "People" list items into separate captures? My understanding was, that once it dealt with the first instance, it would consider its work done, and stop.

-marko.


Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 01, 2020, 01:45:46 pm
Try this for some magic:
Code: [Select]
Replace(Replace(ListMix(if(Compare(find(/#[L1]#/,!People\),>=,0),Replace(/#[L1]#/,!People\,),@@), 0, [keywords]),; @@,),@@; ,)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 01, 2020, 02:13:17 pm
I think there's a bug in the the ListMix function because, if there are brackets in the list items, a syntax error is produced, only for that item, the rest are processed correctly.

It looks like parenthesis were causing problems.  Next build should fix it.  Thanks for the report.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on June 01, 2020, 02:48:24 pm
Still taking requests? If so, if there isn't already a way, I'd love to be able to read environment variables into a couple of my expressions.

Also, I've recently hit a situation where the ability to do date calculations would be super helpful. Even if you could just clue me in to how to manipulate the internal floating-point value to add/subtract seconds?
Title: Re: Expression functions [HELP WANTED]
Post by: mattkhan on June 01, 2020, 03:01:14 pm
Even if you could just clue me in to how to manipulate the internal floating-point value to add/subtract seconds?
It is days since epoch where epoch is December 30th, 1899 at 00:00:01

+1s is therefore +1/86400
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 01, 2020, 08:10:25 pm
Still taking requests? If so, if there isn't already a way, I'd love to be able to read environment variables into a couple of my expressions.

I'll add this tomorrow.  Thanks for the suggestion.
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 01, 2020, 08:12:52 pm
Also, I've recently hit a situation where the ability to do date calculations would be super helpful. Even if you could just clue me in to how to manipulate the internal floating-point value to add/subtract seconds?

Also, if you want to do math on the floating point number, you need to use the raw format of the field. i.e. [Date,0] shows the raw floating-point number.

So to add 35 seconds to a date stored in the [Date] field: Formatdate(Math([Date,0]+(35/86400)), Date HH:mm:ss)
Or to add 35 seconds to the current time: Formatdate(Math(Now()+(35/86400)), Date HH:mm:ss)  That works because Now() is a function not a field, and returns a floating-point number and not a formatted date.

Also, I think the epoch begins at December 30th, 1899 at 00:00:00, as best I have determined it from past testing in MC. The floating-point number calculation never yields an exact zero value. But what is a second among friends?  8)
I always thought that was a strange date and time to start from, rather than December 31th, 1899 at 24:00:00 (January 1st, 1900 at 00:00:00), but maybe that was an error in counting leap years or something... Oh, I see it is the Microsoft COM DATE (https://en.wikipedia.org/wiki/Epoch_(computing)) epoch, and the day shift was because of a leap year!
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 01, 2020, 09:15:21 pm
The tooltip for ListMix() includes information and examples from FormatNumber. A copy/paste error.
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 01, 2020, 11:52:12 pm
Try this for some magic:
Code: [Select]
Replace(Replace(ListMix(if(Compare(find(/#[L1]#/,!People\),>=,0),Replace(/#[L1]#/,!People\,),@@), 0, [keywords]),; @@,),@@; ,)

I tested and deconstructed this expression because I thought I would learn something.

Some notes;
1. This expression will only work correctly on MC26.0.84 and earlier, because of this change in MC26.0.85.
Quote
26.0.85 (5/27/2020)
5. Changed: Standardized on ";" for the list delimiter instead of "; " for all the expressions.
2. While it might be the safest thing to do, and doesn't seem to change the result, I found that the [L1] components didn't need to be escaped using the /#...#/ pair.
3. This works on the sample [Keyword] list Marko provided. I didn't test beyond that.
4. The expression "fails" and returns "@@" when no instances of "!People\" are found. Fixed by wrapping another Replace() function around it.

What I learned;
a. I was trying to work out what the @@ operators were supposed to do... then realised that they are just placeholders for the Replace functions later.
b. This only works because ListMix() is a list manipulation function, and passes the [L1] value down to functions within its scope.
c. This type of expression does my head in, and I have to read it s.l.o.w.l.y.
d. Version 2 of the Syntax Highlighter for JRiver Expression Language (https://yabb.jriver.com/interact/index.php/topic,125150.0.html) makes reading this code so much easier. Thanks Wer!

So on MC26.0.85 and above, use;
Code: [Select]
Replace(Replace(Replace(ListMix(if(Compare(find(/#[L1]#/,!People\),>=,0),Replace(/#[L1]#/,!People\,),@@), 0, [keywords]),;@@,),@@;,),@@,)or
Code: [Select]
Replace(Replace(Replace(ListMix(if(Compare(find([L1],!People\),>=,0),Replace([L1],!People\,),@@), 0, [keywords]),;@@,),@@;,),@@,)

Thanks for sharing Zybex.

But we really need to get the Regex mode -2 tested and working... which is what I was going to look at today... but Rabbit Hole, down I went.  ;D
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 02, 2020, 02:06:01 am
But we really need to get the Regex mode -2 tested and working... which is what I was going to look at today... but Rabbit Hole, down I went.  ;D

The reason I proposed what has been christened the -2 mode in the first place was for exactly situations like this.  Without it, the Regex() cannot take an industry standard regex expression and return all matches at once against a variable length string.  The -2 mode should make operations like this simple.

I haven't been able to test it however because it's still in beta only.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 02, 2020, 02:16:20 am
Thanks RoderickGI, nice analysis :)

The change to ";" will surely break some code out there. I agree with it though.
I really dislike chained functions like Replace(Replace(Replace...), but most times it's the only way. For me it highlights the need for some extra functions/modes:

- Trim(expression) could take a second argument to Trim that instead of whitespace. Trim(expression, ; ) would trim semicolons on both sides

- ListClean() needs a mode 2 to remove empty elements from a list

- ListGrep(list, value) (https://yabb.jriver.com/interact/index.php/topic,124543.msg869886.html#msg869886) to return list elements containing "value". It would simplify this expression tremendously. Alternative name: ListFilter()

Regex mode -2 can be used to do this, but it would need some really awkward regex expressions in most cases.

Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 02, 2020, 02:54:32 am
The -2 mode should make operations like this simple.

It should work, but the regex expressions needed can become quite tricky. For this case, it would be something like this (The RemoveRight() it to remove the last semicolon):
Code: [Select]
RemoveRight(Regex([Keywords],/#[;^\s]+!People\\([^;]+;)#/,-2), 1)
Or a general expression to filter a list for elements containing "word":
Code: [Select]
RemoveRight(Regex([Keywords],/#[;^\s]+(.*word[^;]*;)#/,-2), 1)
I've just tested this on the latest beta and mode -2 only returned the first match. Seems it's not working 100% yet.

EDIT: Perhaps I misunderstood how -2 works or how it's implemented. After some more tests, I see it's currently working like this:
a) Regex(this 123 sample 456 text, /#(\d+)#/, -2)               => this outputs 123 (expected: 123;456)
b) Regex(this 123 sample 456 text, /#(\d+)\D+(\d+)#/, -2)       => this outputs 123;456

I expected that -2 mode with a) would output all \d+ occurrences.
The b) case is not as useful as it requires you to know the exact layout of the string you're searching. It's not possible to apply -2 to a list of random length to filter the elements, for instance - my regex examples above to filter the lists will not work with this current -2 mode.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 02, 2020, 03:37:31 am
This is a teaser for something I'm doing - yesterday I started building an interactive Expression Language editor where you can just type and immediately see the results.
This is still very unpolished (half-day work), but check out the 2 screenshots. One shows a rendered Tooltip expression as it would show on the Movie Tooltip view, the other one is a scratchpad I used to test several different expressions.

Planned features:
- instant evaluation (by MC via the API, I'm not reimplementing the language)
- syntax highlighting, color customizable
- rendered (HTML) and Text output views
- HTML tag support: font, b/u/i, img with full src path or "tooltip:" for thumbnails, logos, etc
- multiple scratchpad tabs (like notepad++)
- test expression against any movie (currently this in running inside ZRatings, and when you change to some movie in the main Datagrid the expression re-evaluates for that movie)
- function argument syntax/help
- insert fields and function templates from a list
- color selector for <font> ... WYSIWYG is possible, but too much work for now.

Hopefully next weekend I'll have some test version available.
Feedback so far?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 02, 2020, 04:03:22 am
EDIT: Perhaps I misunderstood how -2 works or how it's implemented. After some more tests, I see it's currently working like this:
a) Regex(this 123 sample 456 text, /#(\d+)#/, -2)               => this outputs 123 (expected: 123;456)
b) Regex(this 123 sample 456 text, /#(\d+)\D+(\d+)#/, -2)       => this outputs 123;456


Zybex, if -2 is working as I intended, it would output 123;456 as per your example A.  That was the whole point of it.

The functionality described in B is useless, because it cannot be used against a string with an unknown number of matches (and it uses non-standard syntax of course).  What you describe in B is also redundant with the already existing R1..R9 functionality.

I can't comment on what Matt actually implemented because I haven't seen the beta, but returning an arbitrary number of matches with a single regex was the goal.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 02, 2020, 09:20:52 am
One new function proposal that would help here:

ListGrep(list, text, mode)

Returns all elements of list which contain text.
Mode 0 - case insensitive
Mode 1 - case sensitive

ListGrep(redneck;blue;red roses, red) = "redneck; red roses"

Then the above code could be reduced to
Code: [Select]
Replace(ListGrep([Keywords], !People\), !People\,)

Next build:
NEW: Added a ListGrep expression function to filter a list to a matching string.

Thanks for the idea.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on June 02, 2020, 11:13:15 am
Next build:
NEW: Added a ListGrep expression function to filter a list to a matching string.

Thanks for the idea.

Brilliant. This will be useful in three or four areas for me. Thank you both.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on June 02, 2020, 11:17:46 am
This is a teaser for something I'm doing - yesterday I started building an interactive Expression Language editor where you can just type and immediately see the results.
<snip>
Hopefully next weekend I'll have some test version available.
Feedback so far?
Looks amazing. Would help enormously when compared to tweaking in notepad++, copy, paste into MC, go back, tweak more, copy, paste, etc.

Will it only work on movie fields?

@Rod,
Thanks for the breakdown and amended expression, which works flawlessly.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 02, 2020, 11:26:21 am
It works on any [field], but it's always in relation to some "File" entity (movie/episode/song/...). Not sure how it behaves with the GroupQuery functions.

The expression is evaluated by MC, so whatever works on MC should work here - unless MC has some extra functionality which is not exposed on the API, which is very much possible.

Still, even like it is I find it's already very useful.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on June 02, 2020, 03:15:47 pm
It is days since epoch where epoch is December 30th, 1899 at 00:00:01

+1s is therefore +1/86400

you need to use the raw format of the field. i.e. [Date,0] shows the raw floating-point number.

D'oh! I had found an earlier thread and/or wiki article that clued me into the 1/86000 of the epoch value but I couldn't ever get the the math to work. It was this last bit about the raw value that I was missing! Thanks guys! This is going to VASTLY simplify a few of my expressions.

I'll add this tomorrow.  Thanks for the suggestion.

Thanks Matt!
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on June 02, 2020, 04:06:56 pm
Is there any way to output information from a different file by referencing its key or another identifier?
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on June 02, 2020, 09:53:22 pm
GroupCountQuery() seems to be a pretty taxing operation for MC to perform on a... largish (200k) library. Could it be sped up at all if we could limit the number of files the query needs to run against? For instance, a query like GroupCountQuery([Album], [Track #]) could be limited to only searching within music files by doing something like GroupCountQuery([Album], [Track #], [Media Type]=[Audio] -[Media Sub Type]=[Audiobook]). Would something like that speed it up?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 02, 2020, 10:03:17 pm
It's puzzling why GroupCountQuery should be significantly slower than the filtering done in a panes view.  It's essentially the same search against the db, and filtering in a panes view is extremely speedy.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 02, 2020, 10:24:52 pm
I can confirm Zybex's findings, that Regex -2 is not working as intended.

The intention was to return all matches of an arbitrary length string that contained an indeterminate number of matches.

For example, in the Boost regex engine (used by MC) we can do the following:

Input: Humphrey Bogart [Role One];Sydney Greenstreet [Role Two];Mary Astor [Role Three]
Regex: [\w\s]+(?=\s\[)
Output(semicolon delimited): Humphrey Bogart;Sidney Greenstreet;Mary Astor

That one regex would output all the actor names in such an input, regardless of whether there was one actor or 20 listed.

The above is exactly what Regex-2 should do.

However, the Regex-2 function only outputs the first match, Humphrey Bogart.

As implemented, to use Regex-2, you must still possess precognition, because you have to know in advance the number of matches that will exist.  This does not work with lists (strings) with a variable number of elements.  You must still provide a number of redundant capture groups EXACTLY equal to the number of matches to be found in that particular string. If you have too few groups, you will not get all matches. If you have too many groups, the Regex() function returns null.  (And that was exactly the problem with the existing R1..R9 functionality.)

This is not helpful, as it is redundant with the pre-existing Regex() functionality and the way it loads results into the R1..R9 variables. Plus, in addition to requiring prior knowledge of the number of captures present, you have to produce a somewhat ridiculous regular expression with multiple redundant capture groups.

The purpose of this enhancement to the Regex() function was to get around those constraints.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 02, 2020, 11:55:43 pm
I've found some issues with GroupSummaryQuery, Matt...

GroupSummaryQuery does not work correctly sometimes IF the field name has spaces in it. 

GroupSummaryQuery(Album Artist,Duration) can be used, but GroupSummaryQuery(Artist - Composition,Duration) can not. [Artist - Composition] is a custom field. Another example, custom field [Artist-Album] can be used, but the identical field [Artist - Album] can not.

The [Artist - Album (Year)] field also can not be used.

My personal favorite because it is so strange:
GroupSummaryQuery breaks (returns null) if the VALUE of the grouping field contains a letter with an umlaut (e.g. Tannhäuser).  So, GroupSummaryQuery(Composition, Duration) works correctly if [Composition]=Tannhauser but breaks if [Composition]=Tannhäuser    A double quote character " has the same effect.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on June 03, 2020, 01:09:40 pm
One new function proposal that would help here:

ListGrep(list, text, mode)

Returns all elements of list which contain text.
Mode 0 - case insensitive
Mode 1 - case sensitive

ListGrep(redneck;blue;red roses, red) = "redneck; red roses"

Then the above code could be reduced to
Code: [Select]
Replace(ListGrep([Keywords], !People\), !People\,)

This is working an absolute treat. Thank you very much to all involved.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 03, 2020, 05:24:48 pm
Maybe the regex needs to empty the array between runs?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 03, 2020, 05:29:50 pm
Maybe the regex needs to empty the array between runs?

I'm not sure what this is in response to.  I don't see the connection to what Zybex and I were seeing with Regex-2.  Is there something we need to explain better?
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 03, 2020, 06:29:40 pm
Could someone post a simple RegEx(..., -2) example that isn't working properly?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 03, 2020, 06:34:41 pm
Sure, that's what my previous post had.  Here it is:

Input: Humphrey Bogart [Role One];Sydney Greenstreet [Role Two];Mary Astor [Role Three]
Regex: [\w\s]+(?=\s\[)
Output(semicolon delimited): Humphrey Bogart;Sidney Greenstreet;Mary Astor

That one regex would output all the actor names in such an input, regardless of whether there was one actor or 20 listed.

The above is exactly what Regex-2 should do.

However, the Regex-2 function only outputs the first match, Humphrey Bogart.

Another example from Zybex's post:

a) Regex(this 123 sample 456 text, /#(\d+)#/, -2)               => this outputs 123 (expected: 123;456)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 03, 2020, 06:46:09 pm
I'm not clear on this example:
Regex(this 123 sample 456 text, /#(\d+)#/, -2)

Isn't there only one capture, so outputting 123 is right?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 03, 2020, 06:52:17 pm
No.  \d+ matches all numbers in the string.  We're trying to return ALL matches.

The current behavior of -2 is functionally equivalent to the existing R1..R9 behavior, in that you have the following problem:

Write a single Regex function that obtains all the numbers from the [Data] list. The data field may have a different number of elements for each file, as follows:

123 Main;1000 Oak;500 people
2000 years;2500 dollars;12 cars;13 ghosts of scooby doo
4 calling birds;3 french hens;2 turtle doves;11 lords a leaping;5 gold rings; 6 geese a laying

Currently, that is impossible. In MC, a different Regex expression is required for each of those lists, to match the exact number of numbers present in each string.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 03, 2020, 06:55:39 pm
Please look at this example:

https://regexr.com/5628d

All numbers are matched (highlighted) with the single expression.

Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 03, 2020, 06:55:56 pm
Matt,
Check the boost::regex_iterator:
https://stackoverflow.com/questions/2593288/how-to-use-c-boosts-regex-iterator

This is about repeating the regex capture on the string until there are no more matches.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 03, 2020, 08:54:56 pm
Exactly.  It's about returning all matches for a capture group, as the /g (global) flag would.

There is also the  match_results::captures function.  Take a look here, in the section on repeated captures:
https://www.boost.org/doc/libs/1_33_1/libs/regex/doc/captures.html

Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on June 04, 2020, 02:28:09 am
We don't use boost. Haven't for years.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 04, 2020, 02:55:52 am
We don't use boost. Haven't for years.
Good for you, that thing is getting bloated.

std::regex_iterator / std::regex_token_iterator does the same.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 04, 2020, 02:58:38 am
Ok then. We were going by a forum post that said the Mac version was being modified to use libboost in order to match the Windows version.  There were also mentions of the Windows version using the MS regex engine.  So is it a secret?

Perhaps your library has regex_iterator.

Regardless of which library you are using, the goal is the same: return all matches.   :)


Edit: I see Zybex and I are posting the same thing at the same time...
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 04, 2020, 08:43:36 am
Well our existing regex implementation just doesn't support returning all the matches.

We're playing with using the regex_iterator.  Too soon to say if it will work, but let's hope.

This thread keeps me busy :)
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 04, 2020, 12:32:23 pm
I'll bet it does.  Thanks Matt.

This thread may be keeping you busy, but it's also made a lot of complicated things much easier for people, and has enabled new things that were previously impossible, so I think it's resulted in a huge improvement in MC.  And you deserve the credit, for starting it and following through on it. :)
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 04, 2020, 05:27:06 pm
We don't use boost. Haven't for years.

What is used now Hendrik?

The Wiki is out of date. It is probably important that people know what version of Regex is used.
https://wiki.jriver.com/index.php/String_Manipulation_Functions#Regex.28.E2.80.A6.29
https://wiki.jriver.com/index.php/Regular_expression_syntax

I'll update the Wiki, if I know what to say in it.
Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on June 04, 2020, 05:37:56 pm
We're using the standard regex implementation available in newer version of C++, using the default ECMAScript grammar. Its the same syntax as the boost regex, just without a megabyte or two of additional dependencies.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 04, 2020, 07:06:44 pm
Here's an idea... I haven't fully thought it through, but I think some people might appreciate it, and and perhaps suggest suggest some refinements...

Beautify(expression): Formats text for nicer display

Beautify would take whatever's in the parens, including expressions, and format it according to the following rules:

Text: Left as is
Numbers: Displayed using the system default thousands seperator, e.g. 12,324
Lists: Displayed with each element seperated by a comma, with an ampersand for the last: Element 1, Element 2 & Element 3
    (some people might prefer "and" but that takes up more space...)


Example: Beautify([Name], released in [Year], and starring [Actors], went on to make $[Gross Revenue].)

Output: Jaws, released in 1975, and starring Roy Scheider, Robert Shaw & Richard Dreyfuss, went on to make $470,654,000.


We have functions already for some of these sorts of things, although nothing simple for making a list pretty, but this would just be a shortcut for people. I didn't include anything for date fields because they already have their own function with exhaustive options.
Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on June 05, 2020, 02:43:24 am
Example: Beautify([Name], released in [Year], and starring [Actors], went on to make $[Gross Revenue].)

That sort of syntax wouldn't work, because the function would be passed one string with the tokens already replaced. Its a basic expression reality, [..] tokens are replaced before the function is called - hence all the escaping shenanigans in those functions that absolute want a full expression, and not replaced values.

The best option would be calling it specifically for every field you want formatted, like eg. the date formatting function works. In your example, for example, you also don't want [Year] to get a thousands seperator, noone understands 1,975 :D

eg.
[Name], released in [Year], and starring Beautify([Actors]), went on to make $Beautify([Gross Revenue]).

Since the limited number of types, personally I would rather have a number and a list formatting function then some opaque "beautify" function. FormatNumber could be expanded to add an option for thousands seperators, just leaving a list function?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 05, 2020, 03:18:03 am
Sure, I don't see why not.  The only part that can't be easily done now is beautifying the list.  So just a list beautify would probably be enough.  It's was just about making it easy for people.
Title: Re: Expression functions [HELP WANTED]
Post by: MikeO on June 05, 2020, 08:27:28 am
I am not sure if its already covered but a "Split" Function

Classical track names tend to be in a format  -- Symphony No. 1 in C Major, Op. 21 : 1. Adagio molto - Allegro con brio -- alas not standardized.

Its common to need to to split off the Composition and the Movement into Custom Tags i.e. either side of the Colon (delimiter) (at the moment it involves a Move/Copy then Find and Replace the bit you don't need.

So an expression where you can define the Split([Tag Name], Delimiter, Left/Right of Delimiter)

"Delimiter" Colon in this case (its often a Hyphen or a Semi Colon
"Left Part" or "Right Part"

Composition = Split([Name], ":", Left)
Movement = Split([Name], ":", Right

A more complex version maybe to index the delimiter in say Beethoven: Symphony No. 1 in C Major, Op. 21 : 1. Adagio molto - Allegro con brio where the Colon appears twice (or n)

Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on June 05, 2020, 08:33:25 am
You can use ListItem() for that, treating your string as a list delimited by a colon, since you can pass a custom delimiter to those functions

ListItem([Name], 0, :) - for the first part
ListItem([Name], 1, :) - for the second part
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on June 05, 2020, 08:36:34 am
And to be precise with your example it needs to be " : " so
ListItem([Name],0,/ :/ )
ListItem([Name],1,/ :/ )

E:  Hendrik was faster...
Anyway here is wikilink:
https://wiki.jriver.com/index.php/List_Manipulation_Functions#ListItem
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on June 06, 2020, 04:07:52 am
ListRemove(list, value, mode) default mode 0=index, 1=value

Remove by index:
ListRemove(a;b;c,1,0) ==> a;c

Remove by value:
ListRemove(a;b;c,b,1) ==> a;c
Would like to promote myself once in case this was  just not seen  8)
So ListRemove() would be useful and also good companion for ListFind()
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 06, 2020, 12:57:09 pm
Agreed, ListRemove would be useful.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 06, 2020, 01:18:03 pm
I'll try to sneak ListRemove into a coming build.  Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on June 06, 2020, 01:41:40 pm
Great.
Thanks Matt!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 07, 2020, 08:36:40 pm
I've been doing some preliminary testing of the new Regex-2 in .90.

Test string:
F. Murray Abraham [Antonio Salieri];Tom Hulce [Wolfgang Amadeus Mozart];Elizabeth Berridge [Constanze Mozart];Roy Dotrice [Leopold Mozart];Simon Callow [Emanuel Schikaneder];Christine Ebersole [Katerina Cavalieri];Jeffrey Jones [Emperor Joseph II];Barbara Bryne [Mrs. Weber]

At first I thought that positive lookaheads weren't working, because
[\w\s\.]+(?=\s\[)
returns no results.

But then I tried:
([\w\s\.]+(?=\s\[))
which is just nesting the above in a capture group, and that returned correct results.

So I'm gathering that Regex-2 will only return a result for an explicit capture group, which I guess is a holdover from the R1..R9 functionality.

Anything else tricky we should be aware of or test for?

Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 08, 2020, 02:52:30 am
I've been doing some more testing of Regex-2, and have found something that might be a bug.

Consider a string with letters and numbers...
a$b%c!

(\W) is a valid capture group for Regex(), and returns $;%;!
(\w) is a valid capture group for Regex(), and returns a;b;c

If you use alternatives with multiple capture groups, the list returned has empty entries (two semicolons together)
(\W)|(\w)     returns   ;a;$;;;b;%;;;c;!;

If you use this form instead, with only a single capture group:
(\W|\w)
you get correct results:  a;$;b;%;c;!

I think Regex(a$b%c!,/#(\W)|(\w)#/,-2)  returning a crapped up list is probably buggy, but at best it seems to me to be bad behavior that will confuse people.

If Regex-2 can only support one capture group when using Alternatives, that is a livable limitation, but I think it would be preferable to fail more gracefully.

Matt could you comment?

Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 08, 2020, 03:56:40 am
I think this behavior is actually desirable. The empty list elements can be removed with some other function (ListClean should have a mode for that...)

Consider this example:
save(Population of Germany is 83M/, France is 67/, UK is 66 millions, var1)
regex([var1],/#(\w+) is (\d+)#/,-2)


ouput: Germany;83;France;67;UK;66

This allows you to get multiple captures from each list item, separated out into a new list.
If you want both captures in the same list item you can just change the regex to have a single capture group, like you mentioned.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 08, 2020, 05:22:01 am
@Matt,
Number() is not capturing negative numbers.
Also not capturing things like "1,234.56" (it returns 1) or "1 234.56" correctly, which are correct ways to represent values, though in this case I'm not sure it should capture.

Perhaps add more args to the function?
Code: [Select]
Number(string, mode, decimalSeparator)mode 0 - strict capture, just -1234.56
mode 1 - flexible capture for things like 1,234.56 or 1 234,56 or 1.234,56, etc

decimalSeparator could default to "."
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 08, 2020, 08:00:11 am
@Matt,
I've mentioned this before, but here goes again - a new mode for ListClean() to:
- remove empty entries - ;; or ; ;
- trim each entry (remove spaces)

Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 08, 2020, 10:27:41 am
Also not capturing things like "1,234.56" (it returns 1) or "1 234.56" correctly, which are correct ways to represent values, though in this case I'm not sure it should capture.

You need to escape commas like this:
Number(1/,234.56)

Otherwise it's another parameter in the function.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 08, 2020, 11:28:15 am
Duh. Silly me.

I thought Number() would always return a type that could be directly used in Math(), but apparently that's not the case - it's still a string.

So Number(1/,234.56) returns 1,234.56, but Math(Number(1/,234.56) + 1) still won't work directly.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 08, 2020, 02:14:50 pm
I thought Number() would always return a type that could be directly used in Math(), but apparently that's not the case - it's still a string.

So Number(1/,234.56) returns 1,234.56, but Math(Number(1/,234.56) + 1) still won't work directly.

Actually I'd forgotten Number() was one of mine!  ;D

I tested and Math(Number([Name])+1)  works correctly.

However, there is a bug in the Number() function:

If [Name]=Concerto No. 1 in B flat: I. Allegro

Number([Name])  returns .
but it should return 1
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 08, 2020, 02:15:29 pm
Matt, GroupCountQuery seems to be working fine now, except for one thing.

If the field to group by is a list with multiple items, the function always returns 0.

So if [Director]=Director 1;Director 2

GroupCountQuery(Director,Name) always returns 0.

If the Director 2 element is not present, so [Director] only has a single element, the function returns the count as expected.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 08, 2020, 09:27:05 pm
Actually I'd forgotten Number() was one of mine!  ;D

I tested and Math(Number([Name])+1)  works correctly.

However, there is a bug in the Number() function:

If [Name]=Concerto No. 1 in B flat: I. Allegro

Number([Name])  returns .
but it should return 1

This is a pretty tricky one because the dot looks like a decimal.  I'm going to try to make the parser smarter tomorrow.  Wish me luck 😄
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 08, 2020, 09:44:33 pm
Well if the string had "No.1" without a space, I could understand returning ".1" but if you look carefully, there is a space in "No. 1" so returning 1 should be unquestionable. And returning just the . by itself must always be an error.

In fact, maybe "No." should be a special case because it is an abbreviation, so that "No.1" without a space would return 1 instead of .1  since I think that is what people would usually want.

I know you'll get it Matt!  Good luck!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 08, 2020, 11:53:06 pm
I just noticed something strange...

GroupSummaryQuery(Album,Track #) works as expected: it reports the total number of tracks (really it just counts different values of Track #, but because each track has a different #, it comes out the same).

But GroupSummaryQuery(Album,Disc #) returns nothing at all.  It should work the same as with Track #.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 09, 2020, 08:44:07 am
I just noticed something strange...

GroupSummaryQuery(Album,Track #) works as expected: it reports the total number of tracks (really it just counts different values of Track #, but because each track has a different #, it comes out the same).

But GroupSummaryQuery(Album,Disc #) returns nothing at all.  It should work the same as with Track #.

Here's the comment from building a summary string for Disc #:
// always return empty for a disc number since there's no fast way to count the number of discs
// and also because sorting links MF_DISCNUMBER and MF_TRACKNUMBER and we'd rather ignore MF_DISCNUMBER
// for grouping files during sorting

So it's by design for now.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 09, 2020, 12:44:36 pm
Next build will have a lot of changes from this thread.  Thanks!

Fixed: ListGrep was not honoring the mode switch for case sensitivity.
Fixed: The example for ListFind had the wrong function name.
Fixed: The number function did not pull negative numbers.
NEW: Added a ListRemove expression.
NEW: Added modes 3 (remove empty strings) and 4 (trim strings) to the ListClean(...) expression function.
Fixed: List fields with multiple values did not work properly with the GroupCountQuery function.
Fixed: The number function searches for actual numbers after a dot so something like (. 1) won't get read as just a dot.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 09, 2020, 01:33:53 pm
Thanks for listening Matt!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 13, 2020, 06:38:57 pm
Hi Matt,

Your GroupCountQuery fix did correct that one specific problem, but there's another case with semicolons:

[Director]=Joe Russo;Anthony Russo
GroupCountQuery(Director,Name) now works ok. This is what you just fixed.

[Director]=Joe Russo
GroupCountQuery(Director;Studios,Name) works correctly, counting matches with both Director and Studio

[Director]=Joe Russo;Anthony Russo
GroupCountQuery(Director;Studios,Name) still always returns 0. This case is still broken.


Additionally, if the Field to Group By contains special characters, the function always fails and returns 0:
[Director]=Alfonso Cuarón
GroupCountQuery(Director,Name)   always returns 0
GroupCountQuery(Director;Studio,Name) always returns 0

The same thing happens with any other accented character, as in François Girard, Josée Dayan, or Måns Mårlind
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 15, 2020, 09:20:33 am
[Director]=Joe Russo;Anthony Russo
GroupCountQuery(Director;Studio,Name) still always returns 0. This case is still broken.

Studio is not a stock field.  Studios is, and using that works for me.

Quote
Additionally, if the Field to Group By contains special characters, the function always fails and returns 0:
[Director]=Alfonso Cuarón
GroupCountQuery(Director,Name)   always returns 0
GroupCountQuery(Director;Studio,Name) always returns 0

The same thing happens with any other accented character, as in François Girard, Josée Dayan, or Måns Mårlind

This should be fixed next build.  Thanks for the report.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on June 15, 2020, 12:28:58 pm
Another of my "Unless somebody already knows a way to do this" requests: As I've been writing increasingly larger and more complex multi-line expressions I often find myself wishing I could "comment my code" to help with all the things that comments usually help with when coding. I'd love some kind of no-op function or expression shorthand that will let me leave comments without affecting the output.

I've been contemplating using either If(0, // This is my comment,) or Left(// I wish there was a better way, 0). They work, but I can't help but think that both of these will have an impact on performance even a little, and that adds up when this is applied to 200k+ files. I've already had to back off some of my more ambitious expressions as they rendered MC unusable, so I'm trying to optimize wherever I can. If you could somehow create a //(This is much cleaner) function that just literally does nothing with its input, that would scratch this weird itch. Again, unless somebody else already has a better way?

Or am I overthinking this and this is just a crazy idea?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 12:30:08 pm
Actually that was just a typo in my post (corrected). Studios was what I was using in the expression column, as I have no [Studio] field without the s, and it was returning positive results for some films and 0 for others, which precludes an error in the actual expression column.

However, today it's working; I don't know what was going on the other day but if I can reproduce it again I'll let you know. It may have had to do with restarts; I had issues with the last update crashing when I loaded a DSP preset when I was testing that, but the problem went away after a reboot so I didn't report it. 
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 12:36:41 pm
Doof, I think maybe you're overthinking it. Have you tried using the Syntax Highlighter? That has helped me make sense of some gnarly expressions.  I think adding comments to short but inscrutable expressions could actually make it harder to read.  Regular expressions don't have comments, and those can be much more opaque than any MC expression.  And as you noted, you can preface everything with a null If() if you really wanted to.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 15, 2020, 12:39:41 pm
I'd love some kind of no-op function or expression shorthand that will let me leave comments without affecting the output.
(...) Again, unless somebody else already has a better way?

The problem is also the whitespace - any end-of-line CRLF is also added to the output.
That said, this is a neat way to add comments:

- create a field called "X" (or anything else), mark it as Hidden (not necessary, just to hide it from regular field lists)
- now you can comment the code like this:
Code: [Select]
This is my actual code [X,this is a comment!]/
code continues here

The slash at the end of line removes the CRLF from the output.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on June 15, 2020, 01:38:56 pm
Doof, I think maybe you're overthinking it. Have you tried using the Syntax Highlighter? That has helped me make sense of some gnarly expressions.  I think adding comments to short but inscrutable expressions could actually make it harder to read.  Regular expressions don't have comments, and those can be much more opaque than any MC expression.  And as you noted, you can preface everything with a null If() if you really wanted to.

The desire for comments is more to help future me remember what's going on. And in this case it actually was a series of regexes that made me wish I could comment what it was they were trying to match against so I could keep them straight from each other.

The problem is also the whitespace - any end-of-line CRLF is also added to the output.
That said, this is a neat way to add comments:

- create a field called "X" (or anything else), mark it as Hidden (not necessary, just to hide it from regular field lists)
- now you can comment the code like this:
Code: [Select]
This is my actual code [X,this is a comment!]/
code continues here

The slash at the end of line removes the CRLF from the output.


Brilliant! This is exactly what I was looking for! Thanks!

I created a library field called simply /. To be honest, I'm surprised MC let me do it. But now I can just do this as my comment [//, This is my comment]/, and it works exactly as expected! Since the / needs to be escaped, I used just a single slash so that when I actually type it I have to use // so it even looks like the type of comment I'm used to. Perfect!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 01:55:26 pm
Neat. That is both clever and unexpected.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 02:22:24 pm
So Matt, going back to Hendrik's suggestion, how about this:

ListBeautify(list): Beautifies a list for output, separating items with commas and the last item with an ampersand.

ListBeautify(A;B;C;D) outputs
A, B, C & D

Just a simple convenience function.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 15, 2020, 03:09:16 pm
So Matt, going back to Hendrik's suggestion, how about this:

ListBeautify(list): Beautifies a list for output, separating items with commas and the last item with an ampersand.

ListBeautify(A;B;C;D) outputs
A, B, C & D

Just a simple convenience function.

Sure thing.  Anything else it should do other than outputting like A, B, & C?

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 15, 2020, 03:15:41 pm
Perhaps call it ListFormat() ?

ListFormat(list, mode, newSeparator)

ListFormat(a;b;c, 0) = a; b; c
ListFormat(a;b;c, 1) = a, b & c
ListFormat(a;b;c, 2) = a, b, & c
ListFormat(a;b;c, 3) = a, b and c
ListFormat(a;b;c, 4) = a, b, and c
ListFormat(a;b;c, -1, //) = a/b/c

Modes 2 and 4 are fillers, really...
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 03:15:51 pm
I'd originally though it might more generally beautify text for output (numbers, etc) but Hendrik didn't think the syntax would work, so the idea was to restrict it just to lists.

I think it looks better if there is NOT a comma before the ampersand.

So A, B, C & D
Not A, B, C, & D

Plus it takes up less space. That's why I used the ampersand instead of and, to conserve space, so to avoid line wraps or exceeding column widths when possible.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 15, 2020, 03:29:54 pm
Thanks guys.  I'll use the ListFormat(...) syntax and allow numbers to specify different things.  If there's one you want once the build ships, just let me know.

Thanks again :)
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 03:40:32 pm
Ok Matt, since you've been making short work of these simple ones we've been giving you, how about a complicated one?  ;D

FieldQuery: Return a list of matches based on a list of fields to search, from a selected scope of files.

Syntax:
FieldQuery(fieldlist,valuelist,return,mode,scope)

Fieldlist: list of fields to search, e.g. Director;Studio

Valuelist: a value to search for each field in fieldlist (could be an expression)

Return: The single field that is returned when a match is found, values from files returned as a ; delimited list

Mode: 0=contains
     1=exact match

Scope: Audio=1/Video=2/TV=4/Image=8  OR'd together  (searches globally)


Examples:

FieldQuery(Director,Ridley Scott,Name,1,2) returns
Alien;Blade Runner;Gladiator

FieldQuery(Actors,Al Pacino,Name,0,2) returns
The Godfather;Heat;Scarface

FieldQuery(Actors;Director,Al Pacino;Michael Mann,Name,0,2) returns
Heat

If you think we should, we could add a final option, context 0=global, 1=current file scope in view)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 15, 2020, 03:57:30 pm
Slight change: no need for mode -1, the custom separator can be on mode 0 instead. This way it also works as a default mode = 0, default sep = "; "

ListFormat(a;b;c, 0) = a; b; c
ListFormat(a;b;c, 0, //) = a/b/c
ListFormat(a;b;c,, //) = a/b/c
ListFormat(a;b;c) = a; b; c
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 15, 2020, 04:03:53 pm
FieldQuery(Director,Ridley Scott,Name,1,2) returns
Alien;Blade Runner;Gladiator

This is a nice one. Makes me think, if you're were using a relational DB you can could just go ahead and add an SqlQuery() function  8) ;D

Code: [Select]
SqlQuery(SELECT Name FROM Files WHERE Director='Ridley Scott' AND /[Media Type/]='Video')
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 15, 2020, 04:45:01 pm
Scope: Audio=1/Video=2/TV=4/Image=8  OR'd together  (searches globally)

Scope needs some more thought, because TV is Video. You are mixing selection based on [Media Type] and [Media Sub Type].

if you're using a relational DB

Unfortunately, MC doesn't use a relational database. If it did, we wouldn't be discussing all this here.
Title: Re: Expression functions [HELP WANTED]
Post by: glynor on June 15, 2020, 04:52:03 pm
Scope needs some more thought, because TV is Video. You are mixing selection based on [Media Type] and [Media Sub Type].=

No. TV is its own [Media Type], for entries in the TV Program Listings. TV Show is a [Media Sub Type], generally used for Videos, but wer is right. He did forget Documents though.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 05:03:02 pm
I didn't forget Documents, I chose not to include it, the same way I didn't include Data or Playlist.  This because those are unlikely to share field contents that would make the search useful, and I wanted to minimize the number of cases.

I suggest we debate details like that after we find out whether Matt tells me I'm off my rocker for even suggesting such a thing, or not.  This is a significantly more complex function than we've asked for previously.   :)
Title: Re: Expression functions [HELP WANTED]
Post by: glynor on June 15, 2020, 05:03:44 pm
I should note, though, that I’m not sure why such a function would need a scope like that at all. That seems like a redundant, and oddball, addition to the Expression Language in a single function.

Why wouldn’t you just limit the scope on the input side with the much more capable Search Language?
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 15, 2020, 06:30:16 pm
No. TV is its own [Media Type], for entries in the TV Program Listings.

Well, not exactly. In the "Television Guide" database program listings have a [Media Type] of "TV", and [Media Sub Type] of "TV Show" or "Movie". I guess if a View or Search in which this function was used included the "Television Guide" database then a [Media Type] of "TV" makes sense.

However in the "Main" and "Playing Now" databases a [Media Type] of "TV" is just TV channel information, and not program information. A [Media Type] of TV isn't valid for the other databases. As searching TV channel information with this new function didn't make much sense, I thought Wer meant the "TV Show" [Media Sub Type] and just wrote that shorthand.

But I guess searching TV program listings using a function like this makes sense, so if the View wasn't restricted in which databases it showed, then yes, including a [Media Type] of "TV" makes sense.

I should note, though, that I’m not sure why such a function would need a scope like that at all. That seems like a redundant, and oddball, addition to the Expression Language in a single function.

Why wouldn’t you just limit the scope on the input side with the much more capable Search Language?

This is I agree with. I didn't like the way Scope was proposed in the function, so I thought it needed more thought.

The function might need some more examples of just how it would be applied as well. I could see a View showing people also listing the Movies and TV Shows they have appeared in, via a delimited list, but I'm not entirely convinced. What issue is the function addressing Wer?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 15, 2020, 06:53:56 pm
I don't want to argue about the scope at this point.  It is obviously redundant with what could be done by including a tag like Media Type in the search, I knew that. The reason I suggested it in the form I did was in the hopes it would speed up the search: depending on the scope parameter, the search would be run against a much smaller subset of files, instead of against all files all the time, which would be necessary if you're explicitly scanning a tag value. I guessed that MC might have internal shortcuts to limit scope to certain types of files in this way.  If Matt says it does not, then clearly the scope variable would be pointless.

The goal of the function is to be able to retrieve and display data about related files, in places where such things are not currently possible, like in theater view.

GroupSummaryQuery and GroupCountQuery summarize and count data for related files, respectively.  This function would enumerate data, instead of counting or summarizing.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 19, 2020, 02:07:24 am
Matt, what do you think of FieldQuery?  Are you game to give it a try?

Ok Matt, since you've been making short work of these simple ones we've been giving you, how about a complicated one?  ;D

FieldQuery: Return a list of matches based on a list of fields to search, from a selected scope of files.

Syntax:
FieldQuery(fieldlist,valuelist,return,mode,scope)

Fieldlist: list of fields to search, e.g. Director;Studio

Valuelist: a value to search for each field in fieldlist (could be an expression)

Return: The single field that is returned when a match is found, values from files returned as a ; delimited list

Mode: 0=contains
     1=exact match

Scope: Audio=1/Video=2/TV=4/Image=8  OR'd together  (searches globally)


Examples:

FieldQuery(Director,Ridley Scott,Name,1,2) returns
Alien;Blade Runner;Gladiator

FieldQuery(Actors,Al Pacino,Name,0,2) returns
The Godfather;Heat;Scarface

FieldQuery(Actors;Director,Al Pacino;Michael Mann,Name,0,2) returns
Heat

If you think we should, we could add a final option, context 0=global, 1=current file scope in view)
Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on June 19, 2020, 03:22:23 am
If Matt says it does not, then clearly the scope variable would be pointless.

I'm not Matt, but there are no magic shortcuts. We internally just use search expressions as well.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 19, 2020, 12:02:59 pm
Ok then...  So this would make more sense:

FieldQuery: Return a list of matches based on a list of fields to search, from a selected scope of files.

Syntax:
FieldQuery(fieldlist,valuelist,return,mode,scope)

Fieldlist: list of fields to search, e.g. Director;Studio

Valuelist: a value to search for each field in fieldlist (could be an expression)

Return: The single field that is returned when a match is found, values from files returned as a ; delimited list

Mode: 0=contains
     1=exact match

Scope: 0=global (searches all files), 1=current view scope (just search the files in the current view, for speed)


Examples:

FieldQuery(Director,Ridley Scott,Name,1,0) returns
Alien;Blade Runner;Gladiator

FieldQuery(Actors,Al Pacino,Name,0,0) returns
The Godfather;Heat;Scarface

FieldQuery(Actors;Director,Al Pacino;Michael Mann,Name,0,0) returns
Heat
Title: Re: Expression functions [HELP WANTED]
Post by: marko on June 20, 2020, 02:14:13 am
ListRemove() does not perform on a substring level, is that by design?

listremove(my;test;expression string,/ string) removes nothing
listremove(my;test;expression string,expression string) removes that last item

Maybe there should be a substring mode 2, so...
listremove(my;test;expression string;this string;that string,/ string) would return:
my;test;expression;this;that

-marko
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 20, 2020, 06:15:16 pm
I tried to do what you are trying with all of the other List Manipulation functions Marko, and couldn't find a way.

The ListFind(...) function includes sub-string matching, such that;
ListFind(my;test;expression string;this string;that string,string)

returns the first match, "expression string".

ListGrep(...) also includes sub-string matching, such that;
ListGrep(my;test;expression string;this string;that string,string)

returns "expression string;this string;that string".

If ListGrep supported a NOT operator, then it could perform the function. i.e.;
ListGrep(my;test;expression string;this string;that string,-"string")
or
ListGrep(my;test;expression string;this string;that string,!string)
or similar, but it doesn't support NOT matching.


So I would think ListRemove(...) should also include sub-string matching.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on June 21, 2020, 02:19:18 am
I have run into a brick wall over And() and Or()

I cannot get them to do anything meaningful. The tooltips say "Tests a list of values and outputs 1 if all/some of them are true"

Tests a list of values for what?
As examples,
And(1,1,1)
Or(1,0,1)
are really not very helpful.

And(2,7,10) returns "1"
And(2,7,Yes) returns "0"
And([Artist],[Album Artist]) returns zero (Here, I was expecting maybe a "1" if both were populated, or perhaps "1" if they were the same, but regardless of variations, it always returns zero.

What am I not understanding?
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 21, 2020, 02:41:35 am
This works:

Code: [Select]
AND(IsEqual([Artist], Leonard Cohen, 8), IsEqual([Album], Leonard Cohen, 8))
See image.

Also this:

Code: [Select]
AND(IsEqual([Artist], Leonard Cohen, 0), IsEqual([Album Artist (auto)], Leonard Cohen, 0))
Title: Re: Expression functions [HELP WANTED]
Post by: wer on June 21, 2020, 02:47:51 am
Exactly.  It's meant to be used in an If, so that you don't have to string lots of ifs together.

Code: [Select]
If(AND(IsEqual([Artist], Leonard Cohen, 8), IsEqual([Album], Leonard Cohen, 8)),All True, Something's false)
It's essentially a convenience function, to make complicated expressions much shorter. IfCase is another example of that.
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on June 21, 2020, 02:56:17 am
Also

Code: [Select]
AND(IsEqual([Artist], Leonard Cohen, 8), IsEqual([Album Artist], Leonard Cohen, 8))
Returns 0, because the [Album Artist] is empty.

But

Code: [Select]
OR(IsEqual([Artist], Leonard Cohen, 8), IsEqual([Album Artist], Leonard Cohen, 8))
Returns 1, because while [Album Artist] is empty, [Artist] contains "Leonard Cohen".
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on June 21, 2020, 04:10:16 am
ListRemove() does not perform on a substring level, is that by design?

listremove(my;test;expression string,/ string) removes nothing
listremove(my;test;expression string,expression string) removes that last item

Maybe there should be a substring mode 2, so...
listremove(my;test;expression string;this string;that string,/ string) would return:
my;test;expression;this;that

-marko
Can be done with Replace(mylist,/ string,,) (Not a List function but anyway...)

BUT what I think could be useful would be mode 2 which removes ALL the items which contains the search query
listremove(my;test;expression string;this string;that string,/ string,2) would return:
my;test
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 21, 2020, 04:34:43 am
Workaround to remove list items which contain X:
Code: [Select]
listclean(listmix(/#if(isequal([L1],moon,8), , [L1])#/, 0, yellow sun;blue moon;green tree),3)

ListGrep() could take 2 extra modes for a negative search (2=does not contain, 3=does not contain insensitive).
I think changing ListRemove() is not needed, as a simple Replace() works for what Marko wants, as Lepa pointed out.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on June 21, 2020, 06:49:17 am
Exactly.  It's meant to be used in an If, so that you don't have to string lots of ifs together.

Code: [Select]
If(AND(IsEqual([Artist], Leonard Cohen, 8), IsEqual([Album], Leonard Cohen, 8)),All True, Something's false)
It's essentially a convenience function, to make complicated expressions much shorter. IfCase is another example of that.
Also

Code: [Select]
AND(IsEqual([Artist], Leonard Cohen, 8), IsEqual([Album Artist], Leonard Cohen, 8))
Returns 0, because the [Album Artist] is empty.

But

Code: [Select]
OR(IsEqual([Artist], Leonard Cohen, 8), IsEqual([Album Artist], Leonard Cohen, 8))
Returns 1, because while [Album Artist] is empty, [Artist] contains "Leonard Cohen".

Yep, OK, thank you. Foggy head this morning, which isn't getting any better...
Title: Re: Expression functions [HELP WANTED]
Post by: jherbert on July 02, 2020, 11:07:09 am
I have loads of files that have name of the track completely or partly repeated up to three times, like the following sample:

1. Allegretto, con grazia - Sinfonie Nr. 1 (1947) (New Version For Chamber Orchestra, 1963) - 1. Allegretto, con grazia - Sinfonie Nr. 1 (1947) (New Version For Chamber Orchestra, 1963) - 1. Allegretto, con grazia - Sinfonie


or

1. Etwas lebhaft und mit der innigsten Empfindung (Allegretto ma non troppo) - Piano Sonata No.28 in A, Op.101 - 1. Etwas lebhaft und mit der innigsten Empfindung (Allegretto ma non troppo)



Apart from being wrong and not nice to look at, this is a real problem as filenames and path lengths get out of Windows limits quite easily. Rename and move generates errors, tags are not written properly etc.

My brute force solution is to replace [Name] with a custom field [Save Name] that trims the content of [Name] to say 72 characters, allowing error free saving most of the time.

[Name] still contains lots of garbage, though.

An expression would be handy that does the follwing:

take the leftmost n characters from [Name]
search [name] for that string, starting at n+1
return the string from leftmost position to the start of the match -1
write new [Name] to custom field [Clean Name]

Unfortunately I have no idea how to implement that, nor do I know if there would be a better solution.

I would really appreciate your help on this. Thank you in advance.
Title: Re: Expression functions [HELP WANTED]
Post by: JimH on July 02, 2020, 11:30:11 am
If your files are named correctly and the error is only in the tag, you could use the Library Tool to fill properties from filename.  Try a few before you do very many.
Title: Re: Expression functions [HELP WANTED]
Post by: jherbert on July 02, 2020, 01:00:15 pm
If your files are named correctly and the error is only in the tag, you could use the Library Tool to fill properties from filename. 

They are not, unfortunately. I generate filenames and directories from tags (lossy/lossless for drive, artist/album for directory, disc/track/name for filename), which works well for 99,99 percent. So this is not an option.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 02, 2020, 02:23:32 pm
Zelda to the rescue :)
This finds the position where the first 20 chars start repeating and then truncates the string there. If there's no repetition, it keeps the first 200 chars - replace the 20 and 200 as desired:

Code: [Select]
clean(left([name], replace(find([name], left([name],20), 20), -1, 200)))
You can use this to generate your [Clean Name], but you will need to rename the actual files to fix your problem.
Title: Re: Expression functions [HELP WANTED]
Post by: jherbert on July 03, 2020, 02:50:54 am
Zelda to the rescue :)
This finds the position where the first 20 chars start repeating and then truncates the string there. If there's no repetition, it keeps the first 200 chars - replace the 20 and 200 as desired:

Awesome. Found 3003 files with repetitive patterns.

Changed 200 to 0, so I could easily find those tracks, used move/copy to replace content of [Name] with [Clean name] for these and finally did a rename based on /artist/album/name.

Thank you so much.

(Though there was quite an amount of data mediacenter proved to be rock solid during the operation)
Title: Re: Expression functions [HELP WANTED]
Post by: marko on July 11, 2020, 11:30:18 am
Still taking requests? If so, if there isn't already a way, I'd love to be able to read environment variables into a couple of my expressions.
Quick question regarding this...

Is this a "Windows only" thing, or do all operating systems have "System Variables", and if the latter, does this expression work on all operating systems MC runs on?

-marko
Title: Re: Expression functions [HELP WANTED]
Post by: wer on July 11, 2020, 12:15:44 pm
Is this a "Windows only" thing, or do all operating systems have "System Variables"

I couldn't say about "all" (which would encompass things like the operating systems for embedded devices) but Windows, MacOS, and Unix/Linux certainly do have environment variables. It's not Windows only.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on July 12, 2020, 04:33:05 am
Thanks wer.

Here's a thing I uncovered regarding GroupCountQuery() (cool function btw) today...

I couldn't figure out why "[artist, 1] (groupcountquery(artist,album,1) albums)" returned "Chris Rea (14 Albums)" but when selected, only 12 albums were listed...
It's because, under my "User" settings, I have an exclusion of -g=Christmas, and "Driving Home For Christmas" is included on two albums, so, although these two Christmas albums are not listed, they are still counted...

Should they be?

I'm happy because I now know what's going on, so I don't really mind either way... incidentally, ~dup also includes items excluded under user settings, but does not display them, which can be potentially, rather confusing.

If pressed to get off the fence, I would side with user settings should be the top level filter and exclusions there should not be considered when performing calculations.

-marko
Title: Re: Expression functions [HELP WANTED]
Post by: wer on July 12, 2020, 12:11:30 pm
GroupCountQuery counts globally, regardless of anything selected or excluded in the view.

It has to work that way, otherwise it would be non-functional in Theater View.  This is because in theater view, when the file info panel is displayed, the current working set is always exactly one file, so the function would always return 1 regardless of circumstances, if it didn't count globally.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on July 12, 2020, 12:21:19 pm
Absolutely, understood, of course, I followed the work you did with Matt closely...

I would still say it should really operate immediately after the user filters. To be clear, I'm talking about filters applied under "View > User > Edit Current User"

As I said before though, it's not a big deal to me either way, just thinking out loud.

-marko
Title: Re: Expression functions [HELP WANTED]
Post by: wer on July 12, 2020, 01:06:35 pm
I think I recall there was some talk about adding an option to make the search global vs the current working set (0/default=global, 1=current set) (which would then do as you describe) so it could be used more flexibly in Standard View, but I don't think it was implemented.  Without such an option I don't see how the expression could tell the difference between "user filters" as you describe vs any other working set limitation, like in theater view or drilling down into a standard view.

Matt would have to comment.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on July 12, 2020, 01:18:12 pm
I wouldn't want current set, I would want global, for theater view...

Thing is, all other Theater View users are oblivious to the fact that there are user exclusions in force, meaning that the results have the potential to confuse and disorientate them.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on July 13, 2020, 07:34:52 am
ListRemove() does not perform on a substring level, is that by design?

listremove(my;test;expression string,/ string) removes nothing
listremove(my;test;expression string,expression string) removes that last item

Maybe there should be a substring mode 2, so...
listremove(my;test;expression string;this string;that string,/ string) would return:
my;test;expression;this;that

-marko

I'll add a mode 2 coming later to substring match.  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on August 17, 2020, 06:26:03 pm
Is there going to be any more with this thread Matt, or are you all done?

Thanks...

Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 17, 2020, 07:47:41 pm
I'm still open to good ideas 😀
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 18, 2020, 03:17:41 am
Here's one that definitely takes more than 5 minutes of your time: user defined functions and function libraries ;D
It sounds complex but it's actually simple, it's mostly string handling/replacement:

function(fname, /#definition#/) - register a function definition (code) for a new function (just saves the definition as a string, no parsing)
call(fname, arg1, arg2, ...) - call a used-defined function (just replaced by the definition on first-pass parsing)
using(fieldName) - load [fieldName] field which would contain a set of function definitions (save() variables could also be there)

The end goal would be to have libraries of user-defined functions that could be developed, maintained and shared by the community.

examples:
Simple example with min/max functions (I know they exist in Math(), this is just an example):
Code: [Select]
function(max, /#if(isequal([P1],[P2],5),[P1],[P2])#/)
function(min, /#if(isequal([P1],[P2],5),[P2],[P1])#/)
Where [P1],[P2]...[Pn] are the function parameters that will be replaced when the function is called (simple string replace)

Usage:
call(max, 10, 15)                 => returns 15
call(min, \[last played\], 1)   => this replaces [P1] with [Last Played], which is then interpreted as the actual field. Should be the same as "call(min, [last played], 1)"

Alternatively (better), you could get rid of Call() by just detecting that this is a known/defined function:
max(10, 15)                        => returns 15, same as call(max, 10, 15)

Function library:
We could store a bunch of function definitions in a Field, for instance in [Functions]. To use this function library in other places we would just import the field at the top of our code:
Code: [Select]
using(functions)/
min(100,10)

Simply doing "[functions] min(100,10)" could also work, but it would produce a few newline chars on the output. Importing with Using() would load the definitions while suppressing all output, avoiding that problem.

The [functions] field could actually be a predefined field for this, and it would be automatically imported if it exists. The user could still import other fields with Using(). This would make all this much easier to use in any place where Expressions can be used, such as for sorting, filtering, etc, without having to explicitly type "using(functions)"

Notes and caveats:
- recursive functions probably cannot be allowed
- functions calling other functions should however work, as long as the call stack doesn't result in a loop
- function definition order should not matter, but the case where F1() calls F2(), but F2() is defined after F1() needs to be handled.
- since this is simple string replacement, arguments can be simple values, lists, fields, expressions, etc.
- performance should not be affected by having large function libraries, as, again, all this would just (mostly) work via string replacement
- redefining a user-function should replace the definition, but redefining a system function should probably not be allowed
- the predefined [functions] fields could be loaded once, systemwide, at MC startup (and reloaded when it's saved/changed) (this would work for function definitions but not for save() calls which require the current context to be a File)
- a library field such as [functions] can also include other using() calls. This way the [functions] would be a global auto-loading field for all user code.

...Too complex?

Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 20, 2020, 01:28:42 pm
Thanks for the idea.  I wonder if functions could just be stored as part of the expression editor (popup dialog).

Making a working min and max sounds fun 😀
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 20, 2020, 01:44:08 pm
As long as they're available globally, where they're stored is secondary (to the users). I think it would be useful to load them from Field(s), so that users could maintain different function libs: [FunctionsMoe], [FunctionsMarko], [DateFunctions], etc. Sort of separate modules that could be updated just by pasting a new version into the computed field.

Another option is for MC to load all *.elm files from some folder at startup (elm = expression language module). This file could then have some header for version number, module name, etc (optional).

Addenda: Function() could take a 3rd argument for the tooltip text - Function(name, definition, tooltip)

Now go enjoy your vacation :)
Title: Re: Expression functions [HELP WANTED]
Post by: wer on August 20, 2020, 03:52:13 pm
I'm still open to good ideas 😀

I'd still encourage on FieldQuery.  It would enable returning related files, which would be especially neat in Theater View...  Do you think it's a good idea?  ;D

FieldQuery: Return a list of matches based on a list of fields to search, from a selected scope of files.

Syntax:
FieldQuery(fieldlist,valuelist,return,mode,scope)

Fieldlist: list of fields to search, e.g. Director;Studio

Valuelist: a value to search for each field in fieldlist (could be an expression)

Return: The single field that is returned when a match is found, values from files returned as a ; delimited list

Mode: 0=contains
     1=exact match

Scope: 0=global (searches all files), 1=current view scope (just search the files in the current view, for speed)


Examples:

FieldQuery(Director,Ridley Scott,Name,1,0) returns
Alien;Blade Runner;Gladiator

FieldQuery(Actors,Al Pacino,Name,0,0) returns
The Godfather;Heat;Scarface

FieldQuery(Actors;Director,Al Pacino;Michael Mann,Name,0,0) returns
Heat
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 24, 2020, 10:17:29 am
Thanks for the idea.  I wonder if functions could just be stored as part of the expression editor (popup dialog).

Making a working min and max sounds fun 😀

I just added the functions min and max.  If there are other simple ones, just let me know.

I'm not sure we'll add the ability to add user defined functions.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 24, 2020, 10:35:09 am
Thanks.
There was already a Math(min(x,y)) and Math(max(x,y)), though I do prefer them outside Math().
Rand() is another function that exists both as standalone and in Math(). Randn() OTH, only exists in Math().
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 24, 2020, 10:40:24 am
Thanks.
There was already a Math(min(x,y)) and Math(max(x,y)), though I do prefer them outside Math().
Rand() is another function that exists both as standalone and in Math(). Randn() OTH, only exists in Math().

Do you think randn is needed?  You can already specify Rand(-5, 5) and get the range.  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 24, 2020, 10:42:20 am
You may want to consider more than 2 args for min/max:
min(a,b,c, ... N)    (more than 2 args)
max(a,b,c, ... N)

You can also add other aggregator functions in the same way: Avg(...) and Sum(...) come to mind.

Additionally, what about this one:
ListMath(list, mode)
mode 0 = min of all list elements
mode 1 = max
mode 2 = sum
mode 3 = avg

Non-numeric list items are valued as zero, or as Number(item)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 24, 2020, 10:43:26 am
You may want to consider more than 2 args for min/max:
min(a,b,c, ... N)    (more than 2 args)
max(a,b,c, ... N)

I had already thought about this.  They both accept any number of parameters.

Quote
You can also add other aggregator functions in the same way: Avg(...) and Sum(...) come to mind.

Additionally, what about this one:
ListMath(list, mode)
mode 0 = min of all list elements
mode 1 = max
mode 2 = sum
mode 3 = avg

Non-numeric list items are valued as zero, or as Number(item)

I'll think on these.  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 24, 2020, 10:44:16 am
Do you think randn is needed?  You can already specify Rand(-5, 5) and get the range.  Thanks.

No, I'm just saying that one exists as standalone and the other doesn't.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 24, 2020, 10:55:09 am
I'm not sure we'll add the ability to add user defined functions.

(https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/google/241/loudly-crying-face_1f62d.png)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 25, 2020, 08:14:24 am
I'd still encourage on FieldQuery.  It would enable returning related files, which would be especially neat in Theater View...  Do you think it's a good idea?  ;D

Yes, I like it.  I'll try to add it to the coming MC27.  Thanks!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 07, 2020, 04:21:30 am
Here's one I'd like:

Repeat(string, count) - repeats the string count times (count >= 0)

Repeat(- , 20) = "- - - - - - - - - - - - - - - - - - - - "
Repeat(★,7)Repeat(☆,3) = ★★★★★★★☆☆☆
Repeat(☆,0) = <blank>
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 07, 2020, 04:21:51 am
And a couple others:

PadLeft(string, length, char) - pad the string on the left
PadRight(string, length, char) - pad the string on the right
string: string to pad
length: desired string length
char: optional, default is space; char to pad the string with
Title: Re: Expression functions [HELP WANTED]
Post by: mvandyke on September 07, 2020, 07:32:20 am
I'm not sure which expression (or the exact syntax) to use to get the follow:

Compare the artist field to artist biography field and return a 1 (or 0) if the artist biography field does not contain any text that matches from the artist field.

example
artist field=John Legend
Artist Biography field does not contain a text string = "John Legend" then return a 1 , If it does contain the artist field then return a 0.

I've looked at the search tags expression and many others and can't quite figure out how to do this.

Thanks in advance for any potential help!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 07, 2020, 07:40:08 am
Compare(Find([Artist Biography],[Artist]),>=,0)     = returns 1 if [Artist Biography] contains [Artist]
Compare(Find([Artist Biography],[Artist]),<,0)       = returns 1 if [Artist Biography] does not contain [Artist]
Title: Re: Expression functions [HELP WANTED]
Post by: mvandyke on September 07, 2020, 11:10:40 am
Compare(Find([Artist Biography],[Artist]),>=,0)     = returns 1 if [Artist Biography] contains [Artist]
Compare(Find([Artist Biography],[Artist]),<,0)       = returns 1 if [Artist Biography] does not contain [Artist]

Thanks that worked perfect - appreciate your help.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on September 12, 2020, 03:42:29 am
Would it be possible to implement function which returns smartlists where file belongs at the time? Like FilePlaylists() but for smarlists.
It would allow access to some statistical information like "most played songs" etc.
Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on September 12, 2020, 04:17:00 pm
Smartlists are re-evaluated every time they are opened, or accessed. So I don't think you could get that data, and if you could it wouldn't be useful as it wouldn't indicate a file has been played.

Use the [Number Plays] field and a new View to work out the most played tracks. Or just sort any View by [Number Plays].
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on September 12, 2020, 05:05:18 pm
Thanks for suggestion but that doesn't work at all for what I had in mind. I'm not after making Views.
What I was thinking e.g. that I would have smartlist of say 50 most played songs. Now I if could determine with expression that does a song in question currently belong to that smart list, I could print it in tooltip/theaterview as "TOP50 songs".
Merely using [number plays] doesn't tell anything about popularity against others songs.

Title: Re: Expression functions [HELP WANTED]
Post by: RoderickGI on September 12, 2020, 05:51:57 pm
Hmmm, whether the solution you described could be provided is definitely a question for Matt.

But I think you could do what you want with an expression. I would have to think about that. I'm a bit busy right now, but the expression would do something like the Smartlist, and return a 1 or 0 based on the Limit function, ~n.

Wrap something around this:
[Media Type]=[audio] ~sort=[Number Plays]-d ~n=50
Title: Re: Expression functions [HELP WANTED]
Post by: wer on January 02, 2021, 07:51:19 pm
Hey Matt, are you up for another one?

ListMatch(): Returns elements of list1 that partial-match list2.

ListMatch is basically a multi-item version of ListFind.

Syntax: ListMatch(list1,list2,mode,delimiter)

    Mode: (optional)
     0: Case-insensitive (default)
     1: Case Sensitive

    Delimiter: Optional, defaults to ;

Example:
ListMatch(Harrison Ford [Rick Deckard];Rutger Hauer [Roy Batty];Sean Young [Rachael];Edward James Olmos [Gaff];M. Emmet Walsh [Bryant],Rick;Sean)
    Returns: Harrison Ford [Rick Deckard];Sean Young [Rachael]

Alternatively, I think ListFind could be expanded to support this functionality, without affecting existing syntax.  The last two arguments are optional, but it would change the behavior if ListFind was given something that looked like a list for list2 as currently it would only find multiple elements if they were complete and contiguous.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on January 03, 2021, 03:46:41 am
Seconded, this is useful.
This can be done by simply treating the second argument of ListGrep() as a list - no new function or modes required.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on January 03, 2021, 04:24:56 am
Indeed, I hadn't noticed how close it was to ListGrep as well.

For the sake of simplicity and minimizing future maintenance efforts, I would be in favor of incorporating this functionality and arguments into ListFind, and dropping ListGrep and ListMatch entirely.  Only one function is needed, and I think ListFind is the best name. And while the name ListGrep makes perfect sense to me, to non-Unix people it's Greek.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on January 03, 2021, 04:41:15 am
Note that ListFind only returns the first match. It would need additional modes to incorporate ListGrep functionality without breaking existing code (return first vs. return all).
Removing existing functions also breaks existing code.
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 06, 2021, 12:22:51 am
Matt, are you still interested in doing expression language enhancements?

ListFind() could definitely use some improvements, like eliminating this limitation:
Quote
"Default" specifies what the output should be if Search is not found. This is ignored if using Mode 1 to retrieve the list index #, where all 'not found' items return "-1".


Also, I was thinking it would be very helpful to have a roman numeral converter:

Roman(21) returns XXI
Roman(VII) returns 7

The two are actually related, because I wrote a Roman Numeral converter using ListFind, but it's cumbersome to use with the limitation mentioned above, or for larger numbers.

ListItem could be improved by taking a -1 to retrieve the last item, so that we don't have to resort to ListCount() plus Math().

It would be nice if ListLimit() would accept a delimiter.


Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 09, 2021, 01:09:41 pm
Coming next build:

NEW: The ListItem(...) expression functions optionally takes a negative number to mean from the end of the list (-1 is last, -2 is second to last, etc.).
NEW: Added a Roman(...) expression function to convert to and from roman numerals.
Changed: The ListLimit(...) expression function takes a delimiter (optional and defaults to semi-colon).
NEW: Added a Repeat(...) expression function to output a string over and over a given number of times.
NEW: Added the expression functions PadLeft(...) and PadRight(...) to pad a string.

Thanks for the suggestions!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 09, 2021, 01:21:10 pm
Awesome Matt, thanks a lot!

What about removing that limitation on ListFind, so that we can specify the output for "not found" in mode 1? It's necessary for use as a lookup table.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 09, 2021, 01:38:33 pm
Awesome Matt, thanks a lot!

What about removing that limitation on ListFind, so that we can specify the output for "not found" in mode 1? It's necessary for use as a lookup table.

Sure!

Next build:
Changed: Made the ListFind(...) expression function support outputting a custom not found string when searching on index (keep blank to output -1).
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 14, 2021, 04:40:13 pm
Here's one that would be a big time saver and save people a lot of complicated combinations of Find/Left/Right/Mid/Length/Math...

Extract(): Returns a portion of a string bounded by another substring

Extract allows the user to easily extract a portion of a SourceString based on surrounding strings.

Syntax:  Extract(mode,SourceString,string1,string2)

Mode is required and is one of the following:
   1: Everything up to and including string1
   2: Everything up to not including string1
   3: Everything after and including string1
   4: Everything after not including string1
   5: Everything between and including string1 and string2
   6: Everything between not including string1 and string2
   
   Modes 1-6 are case insensitive. Adding 10, for modes 11-16, makes it case sensitive.
   
Examples:

[Name]=Orchestral Suite No. 3 in D major, BWV 1068: 1. Ouverture

Extract(2,[Name], in)
Returns: Orchestral Suite No. 3

[Name]=String Quartet No. 11 in F minor Op. 95 -==Serioso==- I. Allegro con brio

Extract(6,[Name],-==,==-)
Returns: Serioso

[Filename]=\\NAS\Share1\Music\CD\Bach\Flac\Concertos for Orchestra\Concerto for 2 Harpsichords - BWV 1062: I. Vivace
Extract(3,[Filename],[Composer])
Returns: \Flac\Concertos for Orchestra\Concerto for 2 Harpsichords - BWV 1062: I. Vivace

Extract(14,Nocturne OP9 NO2,NO)
Returns: 2


These could of course be done with Regex(), but since many find that too intimidating or difficult, this will make it much more approachable.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 14, 2021, 04:49:45 pm
Thanks for the suggestion!
Title: Re: Expression functions [HELP WANTED]
Post by: glynor on March 14, 2021, 08:42:06 pm
Preface to say, I love wer's suggestion for Extract(). That would make many of my uses of ListItem() much more clear, and require less regex for a relatively simple operation. The expression language can already effectively do the reverse, but can't do Extract between two different delimiters like this simply. However...

Question on the modes for this: Why do you need versions that include string1 and/or string2 in the output?

By definition, in order to use the Extract() function, you'd have to already have those in order to include them as arguments (or at least you'd have them as the output of an expression). So, if you want to include the opening/closing delimiters, you could just re-add them after the "extraction". So all the odd modes are somewhat superfluous. And you don't need both strings at all if you are searching from the beginning or to the end of the Source String (you only need one, like using ListItem() for the same task).

I'm just thinking it would be simpler to use if it was more like this:
Extract(SourceString,String1,String2,CaseSensitivity)

CaseSensitivity:
0: Case in-sensitive (default)
1: Case sensitive

If String1 or String2 are omitted, then the search happens from the beginning or end of the string relatively (and the substrings are never included).

If you want everything between String1 and String2 (case in-sensitive, probably the most common use-case, and equivalent to your mode 6) then:
Extract(SourceString,String1,String2)

If you want everything from the beginning of SourceString up to String2 (case in-sensitive, mode 2), then you do:
Extract(SourceString,,String2)

If you want everything from String1 to the end of SourceString (case sensitive, mode 14) then:
Extract(SourceString,String1,,1)

And, as I noted above, you have String1 and/or String2 by definition in order to use the function. So, if you want the same thing as above but you want to include String1 (case sensitive, mode 13), then you just do:
String1Extract(SourceString,String1,,1)

Then you don't need all the complicated modes, it allows you to default things not needed, and you can still accomplish all the same things, right?
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 14, 2021, 09:50:28 pm
Thanks for the thumbs up, Glynor.  I was of course aware of what you're describing. I proposed it the way I did for the reasons of ease and aesthetics. And ease and aesthetics (as in readability) are the reasons for this function to exist at all, since it can be done with regex anyway. I'll make my case/share my thinking with you and we'll see if you buy it.  ;)

The options of omitting one of the boundary strings I don't favor because although shorter, it is harder for people to understand and remember; it's less intuitive.  If you provide string1 and string2, then string2 represents a terminal boundary, but if you only provide string2, then string2 represents an initial boundary.  That can be confusing for people: that the meaning of the argument changes based on the presence or absence of arguments.  Likewise, I think the ,, notation where arguments are omitted is something that often catches people in mistakes: it's a hard error to spot.

The options for including or omitting the string1 & string2 in output is just to make the function shorter and visually simpler.  Because what if string1 and string2 are not short simple strings, but actually long complex functions or chains of functions?  You'd have to type them over again to repeat them in the output.  I think there's enough of that in the expression language already.  It's a major drag when they're long. Not to mention that if you want to edit the expression afterwards, in your syntax you would have to edit it in the output string as well, whereas in mine you just edit it once.  So my way is I think potentially a huge step forward for ease debugging.  I think it would be a shame to not have it.

I could actually make an argument for a different syntax that makes even more sense than yours:
Extract(mode,string1,SourceString,string2)
And incorporating the idea of omitting string1 or string2. But again, it relies on ,, for omitted arguments, so I won't argue in favor of it, other than to say it is more linguistically consistent than yours: is there a leading boundary, or a trailing boundary, or both. Get it?

Then you don't need all the complicated modes, it allows you to default things not needed, and you can still accomplish all the same things, right?

So remember, since everything this function does can be done by other functions anyway, I think any "you can still accomplish the same thing without it" argument is only valid when viewed in the context of ease of use and "straightforwardness". 

What you describe of course could work, but I think the syntax of what I described is more straightforward, and it unquestionably results in shorter, easier-to-read expressions.

Arguably having a separate option for case-sensitivity is more straightforward than my "add 10" idea. But I liked the add 10 idea; it's like the old days of setting a bit in an options flag byte, except we're doing it in base 10 instead of base 2. :)

So I prefer my original syntax to yours, as I think it will be easier for users. But users will be glad if anything is implemented at all.
Title: Re: Expression functions [HELP WANTED]
Post by: glynor on March 14, 2021, 11:26:43 pm
Yeah. I just look at it differently. To me, it helps with the overall expression readability to avoid the use of "modes" when possible, and to limit the options in them when needed. I'll always have to look those up and can't "intuit" how they'll function. And, relating to the "empty argument" issue, with your method you wouldn't avoid the omitted arguments, you'll just be pushing it to String2, since there will be many modes where String2's value is ignored I guess? And I feel like that ambiguity is a bit clumsy too (is an empty String2 required even when it is ignored, is it an error if you do include it when you "shouldn't" or is it silently ignored, etc) and it is solved by avoiding the modes.

As far as readability, since the delimiters you're looking for would be right in the overall expression (and would almost always be things like parenthesis, brackets, and dashes) when you want them included, I think it would look reasonably clear when you want them included, as in:
{Extract([Album],{,})}
vs not:
Extract([Album],{,})

To me, that's more obvious and elegant than having to remember one of 12 mode numbers. At a glance, what is the output from this:
Extract(5,[Album],{,})
Versus this (which is kind of an error):
Extract(2,[Album],{,})

But, I can see what you mean from your perspective. I just didn't know if I was missing something and wanted to know why you proposed it that way. I agree that either method would work, but I still like mine better. ;)

Matt will probably pick a third way, but I'm not bothered!
Title: Re: Expression functions [HELP WANTED]
Post by: wer on March 15, 2021, 12:33:16 am
Doesn't the editor show you the modes in the tooltip anyway?

I understand where you're coming from.  But I like things that stay simple, even when the search gets more complicated.

Because:

Extract(5,[Name],ListItem([Name],2,-),ListItem([Name],5,/ ))

is a lot simpler than:

ListItem([Name],2,-)Extract([Name],ListItem([Name],2,-),ListItem([Name],5,/ ))ListItem([Name],5,/ )

My syntax (above) and yours (below) are equivalent. Personally, I'd rather debug mine.  ;D

Anyway like you said, we'll see what Matt does...
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 21, 2021, 11:58:43 am
Matt, I think you may want to add a sanity limit to Repeat() ... I was testing some expression and ended up with Repeat(test, 100000000) ... 100% CPU for a while :)
Better limit the output buffer size, not just the repeat count.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 23, 2021, 08:03:51 am
Matt, I think you may want to add a sanity limit to Repeat() ... I was testing some expression and ended up with Repeat(test, 100000000) ... 100% CPU for a while :)
Better limit the output buffer size, not just the repeat count.

Oh boy!  Next build:
Changed: Added a sanity check to the Repeat(...) expression so it will only repeat 100 times then output "... (x more)".
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 23, 2021, 11:33:16 am
Maybe 1000? Or count=Min(count, 1000/len(value))   (careful with div/0)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on March 23, 2021, 01:29:20 pm
Maybe 1000? Or count=Min(count, 1000/len(value))   (careful with div/0)

Even one letter 100 times makes a huge string.  So I think that's a fine cap.  It outputs that there is more.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on March 23, 2021, 02:47:08 pm
The point was more about the total output size.
Repeat(-,100)                  -> 100 chars
Repeat([Filename],100)    -> 10 or 20KB

Yes, I know, just don't do that...  ;D
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 06, 2021, 02:21:07 pm
Just moved this to the v28 board and would welcome any more ideas.  Thanks :)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on May 07, 2021, 05:41:44 am
Thanks for keeping this up :)

Are you willing to take on this one (https://yabb.jriver.com/interact/index.php/topic,128134.msg888967.html#msg888967)?

TreeNode(mode) - Basically, a function to return the name of the currently selected tree node, or the current node being built/enumerated for a View. This would provide context to the expression to allow customization of Views such as what that thread describes.

- When used in a GroupBy/Display expression of a View, returns the tree node currently being created
- When used elsewhere, returns the currently selected tree node

mode=0 : name of current node (eg, "Action")
mode=1 : name of parent node (eg, "Movies")
mode=2 : full path of current node (eg, "Video\Movies\Action")
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 07, 2021, 08:19:28 am
Thanks for the suggestion!  I'll work on it today.

Please keep them coming :)
Title: Re: Expression functions [HELP WANTED]
Post by: DrKNo on May 08, 2021, 12:46:47 am
I would really appreciate a slice-like  expression for Strings. Mid() takes an index and a number of characters, but find() returns indices. If I need to extract based on indizes I always need to invoke additional math().It would be really neat to have an expression that takes to indices and returns the string between them, very much like the Python String[begin:end]. Could look like Slice(String, begin, end). Thank you!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on May 08, 2021, 03:13:46 am
DrKno, check the new Extract() function a few posts above, it may do what you want since it combines Find and Mid/Left/Right.
https://yabb.jriver.com/interact/index.php/topic,124543.msg894801.html#msg894801
Title: Re: Expression functions [HELP WANTED]
Post by: DrKNo on May 09, 2021, 01:20:33 am
Thank you Zybrex! Extract is very useful, thanks for pointing it out. However, it just moves the math problem, as I would still need to convert the index to a string length to use with right(), so it doesn't really affect this use case.

I guess it's not that important in the end since it can be done by subtracting the last index from the length of the string. I'm just lazy :)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 11, 2021, 07:25:46 am
I would really appreciate a slice-like  expression for Strings. Mid() takes an index and a number of characters, but find() returns indices. If I need to extract based on indizes I always need to invoke additional math().It would be really neat to have an expression that takes to indices and returns the string between them, very much like the Python String[begin:end]. Could look like Slice(String, begin, end). Thank you!

I'll try to add this:
NEW: The Mid(...) expression function takes a mode so that you can pass the end as an index instead of a count of characters.

Thanks for the suggestion.
Title: Re: Expression functions [HELP WANTED]
Post by: EnglishTiger on May 12, 2021, 05:40:07 am
Hope fully I'm asking this in the right thread.

As a Classical Music fan I've started using Neo's Auto Parsing methods - https://yabb.jriver.com/interact/index.php/topic,128884.0.html (https://yabb.jriver.com/interact/index.php/topic,128884.0.html)

Using this expression to extract the Composition from the track Name
Code: [Select]
If(IsEqual([Genre],Classical,1),ListItem([Name],0,:),)
and this one to extract the Movement Name
Code: [Select]
If(IsEqual([Genre],Classical,1),ListItem([Name],1,:),)
Which usually does what I wanted to happen - The part of the track Name before the : is placed in the Composition field/tag and everything after the colon is placed in the Movement Name field/tag if present.

However what I've noticed is that if there is a : inside the Movement Name area that expression is truncating the movement name at the :
For this track - "Piano Sonata No. 2 In A minor; Op. 2/2: 4. Rondo: Grazioso"
the Composition field/tag ends up as " Piano Sonata No. 2 In A minor; Op. 2/2"
BUT the Movement Name ends up as "4. Rondo:" - NOT "4. Rondo: Grazioso"


Have I go the expression wrong; have I come across an undocumented constraint/restriction with regard to ListItem, or have I somehow fallen foul of a mistake/error in the coding of the routines ListItem uses?
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on May 12, 2021, 06:19:52 am
Wrong thread, this one is for new feature requests - the thread you linked would be better.

ListItem([name], item#, divider) - this splits the [name] on all occurrences of the divider (colon, in your case) and returns the part number 'item#'.
If the name has 1 colon, there will be 2 items; if it has 3 colons, it will have 4 items. So what you're seeing is normal - it's getting the part between the 1st and 2nd colons.

You can change the Movement Name expression to get everything after the first colon using Regex:
Code: [Select]
If(IsEqual([Genre],Classical,1),trim(Regex([temp],/#:(.+)#/,1)),)
Or using the new Extract() function:
Code: [Select]
If(IsEqual([Genre],Classical,1),trim(Extract(4,[Name],:)),)
The extra Trim() gets rid of leading/trailing spaces.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on May 18, 2021, 09:47:41 am
This has been a long thread (thanks!) so maybe bump the thread if there's a request you're still hoping to see happen.  We've covered a lot of ground and made great progress because of all your help.  Thank you!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on May 18, 2021, 01:28:00 pm
This one would allow people to build and share function libraries:
https://yabb.jriver.com/interact/index.php/topic,124543.msg877195.html#msg877195
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 02, 2021, 03:45:09 am
Hi Matt,
Regarding FieldQuery (already implemented), would it be possible to add a couple of modes to make it do AND-matching instead of OR?

The current implementation returns matches in ANY of the given fields (OR). For instance, this returns all files where Sean Penn OR Nicole Kidman is in the Director OR Actors field:
Code: [Select]
fieldquery(Director;Actors, Sean Penn;Nicole Kidman, Name, 1, 0)
The 2 extra modes would make it match the argument lists, returning files where Director=Sean Penn AND Actors=Nicole Kidman (this one (https://www.imdb.com/title/tt0373926))

Modes:
0=contains any/OR
1=exact match any/OR
2=contains each/AND *new*
3=exact match each/AND *new*

Modes 2/3 require that both list arguments are of the same size - if they're not, then just ignore the extra items on the larger list (user error).

Source problem: https://yabb.jriver.com/interact/index.php/topic,129618.msg899282.html#msg899282
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 03, 2021, 01:18:15 pm
Strings that look like a field and have a comma inside are being truncated by MC when used in an Expression. For instance, if the [Actors] field contains "Hugh Jackman [Logan, Wolverine]", it gets truncated to "Hugh Jackman [Logan]" when used in an Expression.

I understand why it does that, but it should return the whole string when it doesn't recognize the field name. Can you please fix it?
Thanks!

Ref: https://yabb.jriver.com/interact/index.php/topic,119385.msg899365.html#msg899365
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on June 03, 2021, 02:11:41 pm
Strings that look like a field and have a comma inside are being truncated by MC when used in an Expression. For instance, if the [Actors] field contains "Hugh Jackman [Logan, Wolverine]", it gets truncated to "Hugh Jackman [Logan]" when used in an Expression.

I understand why it does that, but it should return the whole string when it doesn't recognize the field name. Can you please fix it?
Thanks!

Ref: https://yabb.jriver.com/interact/index.php/topic,119385.msg899365.html#msg899365

You're right.  It's thinking it's a parameter after the field.  Maybe if it's not a number we can let it pass?
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on June 03, 2021, 02:16:22 pm
I think it should keep the whole thing if it doesn't recognize a valid field, regardless of it being a number or not. If it's a mistake it's up to the user to correct it.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on July 13, 2021, 08:07:39 am
Just bringing this to the top in case anyone has more ideas.  Thanks for all the help so far :)
Title: Re: Expression functions [HELP WANTED]
Post by: EnglishTiger on July 27, 2021, 07:40:47 am
A Trim Option that replaces multiple empty lines with a single empty line, mainly for use when editing Lyrics.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on July 27, 2021, 08:00:04 am
A Trim Option that replaces multiple empty lines with a single empty line, mainly for use when editing Lyrics.

Look at TrimLines(...):
https://wiki.jriver.com/index.php/String_Manipulation_Functions#TrimLines.28.E2.80.A6.29
Title: Re: Expression functions [HELP WANTED]
Post by: EnglishTiger on July 27, 2021, 09:31:02 am
Look at TrimLines(...):
https://wiki.jriver.com/index.php/String_Manipulation_Functions#TrimLines.28.E2.80.A6.29

I did and when I tried TrimLines([Lyrics],1) on these lyrics:-

Time is a wicked master
Put your life into its hand
Close your eyes and it will crush you
Fate only spins you faster
It's a curse born unto man
Turns your dreams into disaster

[Chorus]
Save me because the world's going to stop
And baby, 3, 2, 1, are you ready to rock?
Come on and wake me, the final curtain will drop
And baby, 3, 2, 1
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock tonight?


Lies, it's the truth I'm after
Let me find it in your eyes
Quick before the moment dies
Cries in a sea of laughter
Weeping gently in my soul
Love's the only thing that matters

[Chorus]
Save me because the world's going to stop
And baby, 3, 2, 1, are you ready to rock?
Come on and wake me, the final curtain will drop
And baby, 3, 2, 1
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock tonight?


(10, 9, 8, 7, 6, 5, 4) 3, 2, 1!

[Chorus]
Come on and save me 'cause the world's gonna stop
And baby, 3, 2, 1, are you ready to rock?
Come on and wake me, the final curtain will drop
And baby, 3, 2, 1, are you ready to rock?

[Chorus]
Come on and shake me 'cause we're in for a shock
And baby, 3, 2, 1, we're fallin' down from the top
Come on and take me, we're goin straight to the top
And baby, 3, 2, 1
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock tonight?

I ended up with this:-

Time is a wicked master
Put your life into its hand
Close your eyes and it will crush you
Fate only spins you faster
It's a curse born unto man
Turns your dreams into disaster
[Chorus]
Save me because the world's going to stop
And baby, 3, 2, 1, are you ready to rock?
Come on and wake me, the final curtain will drop
And baby, 3, 2, 1
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock tonight?
Lies, it's the truth I'm after
Let me find it in your eyes
Quick before the moment dies
Cries in a sea of laughter
Weeping gently in my soul
Love's the only thing that matters
[Chorus]
Save me because the world's going to stop
And baby, 3, 2, 1, are you ready to rock?
Come on and wake me, the final curtain will drop
And baby, 3, 2, 1
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock tonight?
(10, 9, 8, 7, 6, 5, 4) 3, 2, 1!
[Chorus]
Come on and save me 'cause the world's gonna stop
And baby, 3, 2, 1, are you ready to rock?
Come on and wake me, the final curtain will drop
And baby, 3, 2, 1, are you ready to rock?
[Chorus]
Come on and shake me 'cause we're in for a shock
And baby, 3, 2, 1, we're fallin' down from the top
Come on and take me, we're goin straight to the top
And baby, 3, 2, 1
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock?
Are you ready to rock tonight?

The only "empty line" it didn't remove was the one at the top of the lyrics tag/field but despite being told to only "Trim double new lines" it also removed the single empty lines, that I didn't want removed, as well. Also if you go back and read my OP again I was asking for an option that replaced multiple empty lines with a singe empty line.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on July 27, 2021, 10:11:56 am
I think what you're looking for is a new mode to convert triple newlines into double.

So next build of MC28:
NEW: Added the number 4 to the TrimLines expression function to replace triple new lines with double.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 27, 2021, 10:33:12 am
Maybe make it replace 3+ newlines with 2? So it would work with larger gaps too.
Ie, "replace 3 or more newlines with 2 newlines"
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on July 27, 2021, 10:36:52 am
Maybe make it replace 3+ newlines with 2? So it would work with larger gaps too.
Ie, "replace 3 or more newlines with 2 newlines"

That's how it will work.  As long as it finds three it will keep replacing.
Title: Re: Expression functions [HELP WANTED]
Post by: InflatableMouse on July 27, 2021, 10:59:45 am
I don't think its currently possible to have an expression manipulate data in another field.

I think I would like it if its possible to have an expression that is able to set a value in a field based on a condition.

Code: [Select]
SetField(condition,field,newvalue,mode)

Mode:
0: trigger SetField to set a new value when the condition does not match
1: trigger SetField to set a new value when the condition matches

I could do something like this:
Code: [Select]
SetField(Find([Album],[MyCustomField],0,0),[Album],[MyCustomAlbum] [[MyCustomField]],0)
Mode is set to 0 so the function works when condition fails.

Album: Best Album Ever! [A67890]
MyCustomField: C12345
MyCustomAlbum: Best Album Ever!
MyCustomAlbum is a calculated data field which strips suffices between () and [] from the album field:
Code: [Select]
Regex([Album], /#^(.+?)((\s*\[[^\]]*\])|(\s*\([^)]*\)))*$#/, 1)

So, SetField in the example above would search the [Album] field for the value in [MyCustomField] and if it doesn't find it, sets [Album] to [MyCustomAlbum] plus [[MyCustomField]].

New Album value: Best Album Ever! [C12345]
MyCustomAlbum: Best Album Ever!

In practise, I would use it update the album field when I change custom fields for Catalog Number and Discogs ID. This would ensure the Album field is always unique, but MyCustomAlbum will always remain the base album title without a suffix.

I suppose this could lead to endless loops and other evil stuff if you're not careful though :P. Some checking should be done to prevent that.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 27, 2021, 11:10:19 am
Where would you type this SetField() expression?
You can do the same by typing an expression like the following in the Album tag:
Code: [Select]
=if(Find([Album],[MyCustomField],0,0),[Album],[MyCustomAlbum] [[MyCustomField]])
I too would like a way to set field values, but the way MC expression language works, it's not simple. There's already a save() function that stores a value on a named variable, so it could just be extended to allow existing field names... but I can see the problems in doing that.
Title: Re: Expression functions [HELP WANTED]
Post by: InflatableMouse on July 27, 2021, 11:15:29 am
Where would you type this SetField() expression?
You can do the same by typing an expression like the following in the Album tag:
Code: [Select]
=if(Find([Album],[MyCustomField],0,0),[Album],[MyCustomAlbum] [[MyCustomField]])
I too would like a way to set field values, but the way MC expression language works, it's not simple. There's already a save() function that stores a value on a named variable, so it could just be extended to allow existing field names... but I can see the problems in doing that.

Thats what Im doing now, or something similar at least but its a manual action for each change I make in one of the custom fields, is manually update the album field with an expression. Its tedious.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 27, 2021, 11:19:08 am
So I ask again, where would you type the SetField() expression? How would it change the workflow?
Note that you can select all files and type that expression on the Album tag to update all of them simultaneously.
Title: Re: Expression functions [HELP WANTED]
Post by: InflatableMouse on July 27, 2021, 11:22:20 am
Ah yeh, that. Or create one for it and move it out of view.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 27, 2021, 11:23:14 am
An out-of-view calculated field is never executed, AFAIK.
Title: Re: Expression functions [HELP WANTED]
Post by: InflatableMouse on July 27, 2021, 11:28:53 am
Then don't but make it really small ;D

I don't know, I admit its not ideal.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 27, 2021, 11:46:06 am
I can tell you the feature I'd like to see and that would also fill your requirement... I've been thinking about this for a while. It could be an MC29 major feature :)

Action Triggers:
- User-defined expressions that get executed when MC does some action on a file
- The expressions could be defined in a XML file, or a specific UI could be added to MC to manage them
- Triggers: BeforePlay, AfterPlay, AfterImport, AfterFieldUpdate, OnViewChange, AfterStartup, BeforeExit, ... (more triggers could be added over time)
- multiple expressions could be defined for same trigger
- A SetField() function would be available on these triggers; this would allow changing fields when the trigger event occurred
- An Exec() function would also be nice to start some external process on these triggers (including MCC commands)
- "AfterFieldUpdate" would take an arg to specify which field would trigger it; user could define triggers for different fields
- Some special vars could provide the [PreviousValue], [NewValue] and [FieldName] on the AfterFieldUpdate trigger
- Using a SetField() on an AfterFieldUpdate event could trigger another expression on a different field... so MC would need to prevent update loops.

Any comment Matt?  :)
Title: Re: Expression functions [HELP WANTED]
Post by: InflatableMouse on July 27, 2021, 12:21:15 pm
It could be an MC29 MC28 feature in the next update  ;D

Corrected that for you  8) ::) ;D

On a more serious note, I thought about suggesting macro's that could run with a hotkey or on a schedule or something. Your action triggers are much better though!

Its a really cool idea!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on July 29, 2021, 12:23:30 pm
Could you give some examples of what you want to do before or after play?

And would the expression run on the playing file?

Would the expression run on every play, or only certain ones?

Keeping me thinking :)
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on July 29, 2021, 01:24:47 pm
Coming next build:
NEW: Added the SetField(...) expression function.
NEW: Added an expression to be evaluated after playback to Options > Library & Folders.

Now you can set after every play!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 29, 2021, 03:27:32 pm
Could you give some examples of what you want to do before or after play?
Mostly setting custom fields, like customizing the Play count or increasing the Rating if a file gets played a lot.

Quote
And would the expression run on the playing file?
Yes.

Quote
Would the expression run on every play, or only certain ones?
All plays.

Quote
Keeping me thinking :)
Thanks for looking into it :)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on July 29, 2021, 03:31:38 pm
Coming next build:
NEW: Added the SetField(...) expression function.
NEW: Added an expression to be evaluated after playback to Options > Library & Folders.

Now you can set after every play!

Awesome  ;D

I would still prefer a generic Trigger/Event mechanism as described above, that you could extend with more triggers over time.
The OnFieldChanged trigger is particularly useful, for instance to add some logic that looks at the new value of field A and decides if/how to update field B based on that. To reduce the number of times it executes, it would need to be a per-field definition and it would only trigger if NewValue != OldValue.

The Exec() function would allow running external commands when something happens; for instance, to update the "now playing" on some 3rd party apps that don't have any other way, or change the screen display mode, or loading some external DSP config, starting some lightshow hardware, etc.
Title: Re: Expression functions [HELP WANTED]
Post by: InflatableMouse on July 29, 2021, 04:05:09 pm
Coming next build:
NEW: Added the SetField(...) expression function.
NEW: Added an expression to be evaluated after playback to Options > Library & Folders.

Now you can set after every play!

Great to hear, thank you!

I would still prefer a generic Trigger/Event mechanism as described above ...(snip!)

I would love to see this too, especially combined with the setfield(). The question Zybex asked me, "where would you type the SetField() expression? How would it change the workflow?" had me thinking, its something I hadn't really thought about to be honest. Having to create a field for the sole purpose of doing some expression magic on other fields seems odd and counter intuitive. A trigger function would solve that when it can load the triggered expression from a file or something, it wouldn't have to sit in a field with no other purpose (it wont display anything by itself so you'd have an empty collumn in your view).

Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on July 29, 2021, 04:16:40 pm
I would still prefer a generic Trigger/Event mechanism as described above, that you could extend with more triggers over time.

We thought about something like this before, using an actual scripting language (maybe something lightweight like Lua, or maybe Python), because expressions are a huge crutch for something like this and are really not suited to actual logic. But its a big project with a very small user base. So maybe it'll happen sometime, or maybe not.

OnFieldChanged in particular would have to be taken with a loooot of care since field changes in some fashion can happen quite in bulk, quite often, as a result of many different processes, be rather performance sensitive .. etc. Its not a callback I would realistically suggest to make use of so lightly.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 02, 2021, 12:35:47 pm
ListFilter(list, mode, minValue, maxValue) - Filter a list, returning only values between a given range

Mode:
0 - numeric
1 - string, case insensitive
2 - string, case sensitive

- MinValue/MaxValue can be blank (no minimum or maximum)
- Both can be empty as well for mode 0 - calling ListFilter(list, 0) returns only the numeric items in the list. For modes 1/2 however it doesn't make much sense, but perhaps it could return only the non-numeric values.

Usecase: https://yabb.jriver.com/interact/index.php/topic,130222.msg903386.html#msg903386

Edit: moved 'mode' to the 2nd arg.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 03, 2021, 02:12:19 pm
Would ListFilter filter strings so you could return everything between Abba and Creedence?

And when it's numeric would the min and max be numbers?

It almost seems like ListFilterString and ListFilterNumber might be cleaner in that case.

And ListFilter in numeric mode with no filters would just return the numbers?  If a string was "a 1 b" what would it do?

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 03, 2021, 02:55:02 pm
Would ListFilter filter strings so you could return everything between Abba and Creedence?
Yes (inclusive both ends, or add modes for inclusive/exclusive; or for min <= X < max (makes sense if you think about it)

Quote
And when it's numeric would the min and max be numbers?
Yes.

Quote
It almost seems like ListFilterString and ListFilterNumber might be cleaner in that case.
Up to you. I like a single function with modes though, more in line with the other functions.

Quote
And ListFilter in numeric mode with no filters would just return the numbers?  If a string was "a 1 b" what would it do?
Right. In your example it would not return anything because the string is alphanumeric.
ListFilter("a 1 b 6 2", 0)           => returns "" (value is not a number, not a list)
ListFilter("a;1;b;6;2", 0)           => returns "1;6;2"
ListFilter("a;1;b;6;2", 0, 2, 10)    => returns "6;2"

Quote
Thanks.
Thank you :)
Title: Re: Expression functions [HELP WANTED]
Post by: marko on August 03, 2021, 03:15:13 pm
and those above would work with raw date data because they are, in effect, just numbers, right?
The convertdate() expressions for min/max values would still be required to get those numeric values?

and... existing list order wouldn't matter either, right?

ListFilter("9;8;1;3;5;7;10;4;6;2", 0, 2, 10) would return: "9;8;3;5;7;10;4;6;2"
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 03, 2021, 03:19:07 pm
Yes, raw dates are just numbers. Which reminds me, it needs to work with decimals/floats too.
Yes, you would need convertdate() to get the min/max values corresponding to given dates... or we could have a mode 2 which filters a list of formatted dates ;)
Yes, order doesn't matter and should be preserved.
Yes, your example is correct (assuming the default is to include min/max values, ie, a<=X<=b)
Title: Re: Expression functions [HELP WANTED]
Post by: marko on August 03, 2021, 03:34:19 pm
Nice to feel that even though I'm sitting up the back, I am paying attention ;)

In a couple of months time once the data has built up, this could get very interesting.
It's so good, I'll think about writing up a wiki page for it if it all pans out as expected.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 04, 2021, 08:07:02 am
Coming to the beta channel later today:
NEW: Added the ListFilter(...) expression function.

Just a note that parsing letters as a number array will just resolve to zero instead of not a number.  Our number array doesn't really understand not a number values, so it'll just be zero.

Thanks for the suggestion :)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 04, 2021, 01:03:07 pm
Cool, thanks :)
letters=0 is fine, it's how it works for most (all?) other functions anyway.
Title: Re: Expression functions [HELP WANTED]
Post by: marko on August 05, 2021, 01:05:53 am
'tis working well. Thanks to you both.

I was poking around with it, seeing how it dealt with as many variations as I could think of, which threw up this question...
listfilter(1;2;3;4;5;a;b;c;d;e;6;7;8;9;10;A;B;C;D;E;1A;A1,0,1,3)
returns 1;2;3;1

Is that trailing "1" correct? I suppose, strictly speaking, it is, but, the list item is 1A.
It's only a test case. I have no real world items to test on, and don't really know what I expected, but know I didn't expect that :)
listfilter(1;2;3;4;5;a;b;c;d;e;6;7;8;9;10;A;B;C;D;E;1A;A1,1,A,B) returns A;B;A1 which seems fine to me.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 05, 2021, 03:11:10 am
Right, I would expect 1A to be ignored. Looks like the function uses sscanf() to extract values, which also works on mixed strings like that. It should first validate if it's a pure number with some sort of isFloat() check.

I found a few other issues with ListFilter(), here's the full list so far:

1. (Marko's finding) Mixed strings starting with a number are accepted, should be ignored in numeric mode:
listfilter(1;2;3A, 0) = "1; 2; 3"    (expected "1;2")

2. it's returning a list with spaces between items - most/all other functions now don't do that
listfilter(1;2;3, 0)  = "1; 2; 3"     (expected "1;2;3")

3. Because strings are evaluated as zero, it returns 0 for non-numeric items if the minValue is not present or includes 0:
listfilter(1;2;A;3, 0) = 1;2;0;3    (expected the zero to be absent)

4. String filtering is not case-sensitive (I think it should not be, or there should be a mode for it):
listfilter(Action;Test;Ball, 1, a, f) = <blank>   (expected "Action;Ball")

5. String filtering uses a non-standard comparison (char by char?), returning more items than expected:
listfilter(Action;Test;Ball, 1, A, B) = "Action;Ball"   (expected only "Action", because "Ball">"B")

Title: Re: Expression functions [HELP WANTED]
Post by: Hendrik on August 05, 2021, 03:19:23 am
I think #5 is easier to understand that way for anyone not super technically inclined. "I want everything from A to B" seems like a trivial question, without knowing the intricacies of string comparisons.
Thoughts? What kind of use-case would the other method favor?
Title: Re: Expression functions [HELP WANTED]
Post by: marko on August 05, 2021, 06:48:45 am
I definately prefer the #5 way as it is at the moment.
Before posting this morning, I was doing some testing using my image keywords field. Specifically, testing using some strings rather than just letters...
listfilter([Keywords], 1, Ac, Br)  would skip Aardvark, returning everything from Acorn to Bridges. All made perfect sense to me :P :D

#4
When I added this to the wiki this morning, I reported that it was case sensitive. It's doing something odd for sure...

listfilter(Action;Test;Ball, 1, A, f) ---> Action;Test;Ball
listfilter(Action;Test;Ball, 1, A, F) ---> Action;Ball
listfilter(Action;Test;Ball, 1, a, F) ---> <Blank>
listfilter(Action;Test;Ball, 1, a, f) ---> <Blank>

Examples 2 & 4 return, for me, expected results that show case sensitivity in operation.
The returns from examples 1 & 3 don't make any sense to me, and again, I didn't know what to expect before I pressed OK. Often the understanding comes from "I wonder what it'll do if I do that...", just not in this case :)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on August 05, 2021, 07:27:02 am
Sorry, I meant to say that it IS case sensitive and I think it should not be, or should have 2 modes to select.
The examples you tried are OK - the uppercase letters come before the lowercase ones in the ASCII table (https://www.asciitable.com/), so their value is lower. So 'A' < 'a' for a computer, and Zebra < banana. This is why I think we need a case-insensitive mode.

Regarding #5, I'm overruled and that's fine.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 05, 2021, 07:42:11 am
ListFilter(...) is using case-sensitive string comparisons now, but I can't think why you'd ever want that.  So I'll switch it for the next build.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 05, 2021, 07:46:31 am
I also got this one for the next build:
Changed: Removed the space from the ListFilter(...) delimiter.

Thanks for testing.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on August 05, 2021, 01:50:33 pm
Changes here:
https://yabb.jriver.com/interact/index.php/topic,130271.0.html

Thanks again :)
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 01, 2021, 11:10:53 am
I can tell you the feature I'd like to see and that would also fill your requirement... I've been thinking about this for a while. It could be an MC29 major feature :)

Action Triggers:
- User-defined expressions that get executed when MC does some action on a file
- The expressions could be defined in a XML file, or a specific UI could be added to MC to manage them
- Triggers: BeforePlay, AfterPlay, AfterImport, AfterFieldUpdate, OnViewChange, AfterStartup, BeforeExit, ... (more triggers could be added over time)
- multiple expressions could be defined for same trigger
- A SetField() function would be available on these triggers; this would allow changing fields when the trigger event occurred
- An Exec() function would also be nice to start some external process on these triggers (including MCC commands)
- "AfterFieldUpdate" would take an arg to specify which field would trigger it; user could define triggers for different fields
- Some special vars could provide the [PreviousValue], [NewValue] and [FieldName] on the AfterFieldUpdate trigger
- Using a SetField() on an AfterFieldUpdate event could trigger another expression on a different field... so MC would need to prevent update loops.

Any comment Matt?  :)

Yes, please to all of the above.

Corrected that for you  8) ::) ;D

On a more serious note, I thought about suggesting macro's that could run with a hotkey or on a schedule or something. Your action triggers are much better though!

Its a really cool idea!

Why not both? ;)

And if we really wanted to get ambitious for MC29 (ok, maybe 30), then we start clamouring for a full UI that allows for users to share Actions that can be applied on a per-file basis using MCs search language rules and run either automatically from an Action Trigger or a user-defined hotkey. :D
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on September 29, 2021, 07:58:06 am
Bump for any more ideas.  Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 29, 2021, 12:09:56 pm
A couple of things I've been really wishing for:

FieldQuery: Some way to perform AND lookups. For example FieldQuery(Series;Season, [Series];1, Date, 1, 2) is an expression I was hoping would return the dates from all files from the same series, but from season 1. Since this does an OR, the results are not what I'm looking for. Not sure if there's another way to do this or not.

I'd really love someway to do a lookup of another file's fields by referencing a filename or filekey. Something like FieldLookup(filekey, fieldname)

And I'd like to again throw my vote in for something like the after-playback expression but for imports. All of the Action Triggers zybex mentions above, really, but I have a specific need for after-import.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 29, 2021, 12:19:05 pm
Quote
I'd really love someway to do a lookup of another file's fields by referencing a filename or filekey. Something like FieldLookup(filekey, fieldname)

Perhaps as a 3rd argument to Field() ? If FileKey is not given, then it's the current file:
Field(name, mode, fileKey)

Title: Re: Expression functions [HELP WANTED]
Post by: leezer3 on September 29, 2021, 12:21:09 pm
Something simple that would be *really* useful here:
The RemoveLeft, RemoveRight etc. functions require a field name.

Change it so that if no field name is supplied, they operate on the current field.
At present, the following: =RemoveLeft(10)
will replace all contents of the field with 10. Similar occurs if we've got a misplaced bracket or something, it just dumps random text into the field, presumably something to do with where it is in the expression tree?
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 29, 2021, 12:28:05 pm
Perhaps as a 3rd argument to Field() ? If FileKey is not given, then it's the current file:
Field(name, mode, fileKey)


Works for me!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 29, 2021, 01:21:41 pm
Change it so that if no field name is supplied, they operate on the current field.
At present, the following: =RemoveLeft(10)

This is likely not possible. As an alternative, what about adding a special field called [this] ?
=removeLeft([this], 10)

If there's no current field in the execution context, [this] would be empty or not replaced (invalid usage).
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on September 29, 2021, 01:36:56 pm
Something simple that would be *really* useful here:
The RemoveLeft, RemoveRight etc. functions require a field name.

Change it so that if no field name is supplied, they operate on the current field.
At present, the following: =RemoveLeft(10)
will replace all contents of the field with 10. Similar occurs if we've got a misplaced bracket or something, it just dumps random text into the field, presumably something to do with where it is in the expression tree?

I know about a current file, but not a current field.  Could you explain what you mean?

Thanks.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on September 29, 2021, 01:37:20 pm
Also, I'm working on this today:
Changed: Made the Field expression function take an optional third parameter to specify a file key (defaults to empty which evaluates the current file).
Changed: The brackets for fields also support the new third parameter.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 29, 2021, 01:40:08 pm
I know about a current file, but not a current field.  Could you explain what you mean?

Thanks.

when an expression is entered on the Tag Editor (or Details View grid) and starts with = , the current field is the field the user is editing.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on September 29, 2021, 01:49:57 pm
I'm working on this too for the next build:
NEW: When setting a field to an expression, a "this" variable is saved so doing something like =RemoveLeft([this], 3) will work.

Thanks for the suggestion :)
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 29, 2021, 01:51:39 pm
What happens if you already have a field called [this]?
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 29, 2021, 01:52:36 pm
Also, I'm working on this today:
Changed: Made the Field expression function take an optional third parameter to specify a file key (defaults to empty which evaluates the current file).

First, YAY!!!

Second, what does this mean?

Quote
Changed: The brackets for fields also support the new third parameter.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on September 29, 2021, 01:52:59 pm
What happens if you already have a field called [this]?

If you have a field called "this" all heck will break loose!  Honestly I think the expression engine would load the "this" field before a user "this" field, so it's probably fine!
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on September 29, 2021, 01:53:47 pm
Second, what does this mean?

This works:
[Name, 1, 543343]

You can call out the file key using brackets or the function.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 29, 2021, 01:55:16 pm
I don't actually have a field called [this], but just curious what the impact would be.

Would something like =RemoveLeft([], 3) be safer? Where empty field name implies current field?
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 29, 2021, 01:55:45 pm
This works:
[Name, 1, 543343]

You can call out the file key using brackets or the function.

AH! That's amazing!
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 29, 2021, 02:16:57 pm
What happens if you already have a field called [this]?
Rename it to [that]  ;D
Title: Re: Expression functions [HELP WANTED]
Post by: SkGe on September 30, 2021, 01:33:51 pm
Food for thought. What if counter() can be change from counting items each time you pass over the field. Like 0 to be default, 1 it counts the items in a list or playlist without changing the number (ex. 1, 2, 3, 4, etc.) and 2 maybe to count as tens and 3 counting as hundreds. Changing this it will help in playlist chart where each a song change the position in the chart but not the number given to that song in playlist.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on September 30, 2021, 02:58:21 pm
Food for thought. What if counter() can be change from counting items each time you pass over the field. Like 0 to be default, 1 it counts the items in a list or playlist without changing the number (ex. 1, 2, 3, 4, etc.) and 2 maybe to count as tens and 3 counting as hundreds. Changing this it will help in playlist chart where each a song change the position in the chart but not the number given to that song in playlist.

Counter already supports setting the start number and the increment.

Is there more it needs?

Counter(1000, 100) starts at 1000 and counts by 100.
Title: Re: Expression functions [HELP WANTED]
Post by: SkGe on September 30, 2021, 03:12:07 pm
Well I didn't know that but reading more carefully it seems so, so yeah. But it is possible if counter() don't increase the number but have a fix one. If let say I have a playlist with 30 songs and I use the counter() in that playlist each time you cross over that song, the number change by design.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on September 30, 2021, 03:29:13 pm
Sounds like you want a Sequence number.
There's this obscure function:
Code: [Select]
customData(#)
If you want increments of 10, just multiply it:
Code: [Select]
Math(10*customData(#))
This probably only works on playlists, not Views.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on September 30, 2021, 04:30:10 pm
Counter already supports setting the start number and the increment.

Is there more it needs?

Counter(1000, 100) starts at 1000 and counts by 100.

MOD?

Title: Re: Expression functions [HELP WANTED]
Post by: Matt on October 01, 2021, 08:56:04 am
MOD?

101 mod 10 = 1

Is that what you mean?  A Mod(...) expression function?
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on October 01, 2021, 09:09:14 am
Math(101 % 10) does that.

I choose to believe that Doof meant MOD as "n. : a Highland meeting for Gaelic literary and musical competitions."  ;D
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on October 01, 2021, 09:55:15 am
Sorry, I meant like a MOD parameter inside Counter() to get it to do stuff like count up to a certain number and then reset back to start. I'm not entirely sure I actually have a use for it though, to be honest.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on October 01, 2021, 10:07:35 am
Sorry, I meant like a MOD parameter inside Counter() to get it to do stuff like count up to a certain number and then reset back to start. I'm not entirely sure I actually have a use for it though, to be honest.

It was easy, so next build:
Changed: Added an optional third parameter to the Counter expression function to set when to start over.
Title: Re: Expression functions [HELP WANTED]
Post by: Doof on October 01, 2021, 10:59:00 am
Lol, I think I'm having more fun experimenting with expressions than I am watching any of my content.

This reminds me of a question I've been meaning to ask. Does anybody actually use the more advanced Math functions like sin, cosin, atan, etc? If so, I'd be really curious to see what use people have found for them.
Title: Re: Expression functions [HELP WANTED]
Post by: Matt on October 05, 2021, 08:16:58 am
Doorbell conversation moved here:
https://yabb.jriver.com/interact/index.php/topic,130868.0.html
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on November 21, 2021, 06:49:38 am
Using Save() / SaveAdd() seems to break ListMix

example:

Code: [Select]
Save(0, v_2021[filekey])/
Save(0, v_2020[filekey])
DATES:
ListMix(FormatDate([L1],yyyy),,44265;44520)

COUNT YEAR 2021:
Load(v_2021[filekey])

COUNT YEAR 2020:
Load(v_2020[filekey])

2020 doesn't exist in list but v_2020[filekey] is still increased on every cycle (both variables are)
ListMix(IfCase(FormatDate([L1],yyyy),2,2021,SaveAdd(v_2021[filekey], 1),2020, SaveAdd(v_2020[filekey],1)),,44265;44520)

COUNT 2021:
Load(v_2021[filekey])

COUNT 2020:
Load(v_2020[filekey])

on similar list mix WAS 2020 is not printed when Save function is not used
ListMix(IfCase(FormatDate([L1],yyyy),2,2021,WAS 2021,2020, WAS 2020),,44265;44520)
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on November 21, 2021, 11:13:55 am
Yeah, ListMix is finicky.
Perhaps you can get the counts you want using ListFilter to get just the items between 2 values:

listcount(listfilter([myList],0,min, max))
Code: [Select]
2020 = listcount(listfilter(44265;44520,0,43831,44197))
2021 = listcount(listfilter(44265;44520,0,44197,44562))

Regarding the ListMix issue you point out - it has to do with the order in which listmix/listmix2 processes the expression. The normal way to process any EL expression is to resolve it from the inside out; that is, process the inner sub-expressions and functions first, and work your way out with the results as you get them. So ListMix() is resolving the inner functions (saveAdd) before actually running the ListMix() function. The IfCase and FormatDate execution is delayed since they depend on [L1], which is only assigned after the ListMix unwrapping.

The difference between ListMix and ListMix2 is the order on which the execution steps are done - functions-first vs unwrapping-first. But there are other implementation details which I don't know about but can see the effects - neither implementation is "pure", they both seem to try to optimize execution with some side effects on some cases, like the one you found. I think Matt has been adding some function exceptions to ListMix(), in that those functions get to be executed after unwrapping instead of before. In that case, adding SaveAdd() to that list would solve your problem... however I don't think this is the right approach.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on November 21, 2021, 11:40:55 am
Yeah, ListMix is finicky.
Perhaps you can get the counts you want using ListFilter to get just the items between 2 values:

listcount(listfilter([myList],0,min, max))
Code: [Select]
2020 = listcount(listfilter(44265;44520,0,43831,44197))
2021 = listcount(listfilter(44265;44520,0,44197,44562))

Regarding the ListMix issue you point out - it has to do with the order in which listmix/listmix2 processes the expression. The normal way to process any EL expression is to resolve it from the inside out; that is, process the inner sub-expressions and functions first, and work your way out with the results as you get them. So ListMix() is resolving the inner functions (saveAdd) before actually running the ListMix() function. The IfCase and FormatDate execution is delayed since they depend on [L1], which is only assigned after the ListMix unwrapping.

The difference between ListMix and ListMix2 is the order on which the execution steps are done - functions-first vs unwrapping-first. But there are other implementation details which I don't know about but can see the effects - neither implementation is "pure", they both seem to try to optimize execution with some side effects on some cases, like the one you found. I think Matt has been adding some function exceptions to ListMix(), in that those functions get to be executed after unwrapping instead of before. In that case, adding SaveAdd() to that list would solve your problem... however I don't think this is the right approach.
Yup. For your suggestion: I was trying to achieve that parsing list is done only once instead of going through list multiple times. Don't know how fast it would be to take ListCount() after every year handling and ListLimit() already handled items out...
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on November 21, 2021, 11:56:52 am
It makes me wonder if things like this wouldn't be best done on a timer; ie, everyday at 4am a script would run, compiling all stats and storing the counts and tops in some field that would then be used in Views. Is it that important to have an up-to-the-minute number of plays for each track since forever? And things like the playcount for past years won't ever change anyway. Updating once a day seems fine for this use case.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on November 21, 2021, 12:19:56 pm
That would be great. If only someone had time to create such thing... Perhaps this kind of stuff could be done with MCUtils scriptor which could then be started with timer. Dunno if it only works for selected files or is possibly select files from scriptor.
Title: Re: Expression functions [HELP WANTED]
Post by: lepa on November 22, 2021, 12:14:27 pm
I think ListReplace(list, index, newItem) to replace one item based on index could be useful. At least i didn't notice that existing list functions support this. One could use ListCount() and ListLimit() I guess to replace item but one clean function would be nice.
Title: Re: Expression functions [HELP WANTED]
Post by: zybex on November 23, 2021, 06:25:11 pm
That would be great. If only someone had time to create such thing... Perhaps this kind of stuff could be done with MCUtils scriptor which could then be started with timer. Dunno if it only works for selected files or is possibly select files from scriptor.

You have an IM  ;D