if var in/contains comma-separated list/array

Post a reply


In an effort to prevent automatic submissions, we require that you complete the following challenge.
Smilies
:D :) ;) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :!: :?: :idea: :| :mrgreen: :geek: :ugeek: :arrow: :angel: :clap: :crazy: :eh: :lolno: :problem: :shh: :shifty: :sick: :silent: :think: :thumbup: :thumbdown: :salute: :wave: :wtf: :yawn: :facepalm: :bravo: :dance: :beard: :morebeard: :xmas: :HeHe: :trollface: :cookie: :rainbow: :monkeysee: :monkeysay: :happybday: :headwall: :offtopic: :superhappy: :terms: :beer:
View more smilies

BBCode is ON
[img] is OFF
[flash] is OFF
[url] is ON
Smilies are ON

Topic review
   

Expand view Topic review: if var in/contains comma-separated list/array

Re: if var in/contains comma-separated list/array

by nnnik » 25 Sep 2018, 17:35

I doubt that will happen in v2.

Re: if var in/contains comma-separated list/array

by iseahound » 25 Sep 2018, 17:07

Hopefully soon

Re: if var in/contains comma-separated list/array

by nnnik » 25 Sep 2018, 12:16

The possibility exists. But not in AHK - at least not yet.

Re: if var in/contains comma-separated list/array

by iseahound » 25 Sep 2018, 11:30

There exists a duality between operators and methods.

See operator overriding in Python: https://thepythonguru.com/python-operator-overloading/
which is a bit of a mess, since they define __add__ to be a meta function to represent +

Re: if var in/contains comma-separated list/array

by nnnik » 25 Sep 2018, 11:24

operators are not methods in AHK. And Strings cannot have string specific methods yet.

Re: if var in/contains comma-separated list/array

by iseahound » 25 Sep 2018, 10:42

nnnik wrote:Also contains is an operator rather than a method call.
There is an equivalence between operators and methods.

Code: Select all

2 + 3
2.+(3) ; strange but should be valid
which seems strange. But consider this code:

Code: Select all

object.push("dog", "cat")
object push "dog", "cat"
which should be command syntax for object methods.

To parse everything correctly:

Code: Select all

obj push "dog", "cat"
obj(push("dog", "cat")) ; obj is not a function and does not take arguments. 
obj.push("dog", "cat") ; try invoking a method of obj. 
So this is the sort of radical change I'd like to see in v2. It would do 3 main things.
- Settle the Command vs. Function syntax argument once and for all by allowing both commands and functions to have the same expressiveness.
- Formalize operators like contains, such that operators are in some sense global methods, and object methods are by default limited to that object
- Allow operator overriding.

I took inspiration from here: https://docs.scala-lang.org/style/metho ... ation.html

EDIT: I made a separate thread https://autohotkey.com/boards/viewtopic ... 37&t=56294

Re: if var in/contains comma-separated list/array

by lexikos » 25 Sep 2018, 06:29

"not in" and "not contains" are a possibility. Python has "is not".

Re: if var in/contains comma-separated list/array

by nnnik » 25 Sep 2018, 01:24

I don't want to add special features for comma separated lists. No argument you have to offer can change my mind about this.

@iseahound
Sadly our "," already has a specific meaning so this Form of calling won't be possible. Also contains is an operator rather than a method call.

Re: if var in/contains comma-separated list/array

by iseahound » 24 Sep 2018, 20:03

Sorry, what's wrong with if var contains "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
which is parsed as if var.contains("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")

As I understand it, contains is a function which takes any number of variables. (variadic)
and contains is a method of var.

Caveats:
Another example: Say I have the following in my code:

Code: Select all

if(foo contains bar) { ..
Say, I need to negate the condition. If I modify it as follows:

Code: Select all

if(!foo contains bar) { ..
Bummer. This gets parsed as (!foo).contains(bar). Not what we wanted.

Or suppose you need to add a new condition in addition, and you modify it so:

Code: Select all

if(foo contains bar && cond) { ..
Another bummer. This gets parsed asfoo.contains(bar.&&(cond)). Not what we wanted, again.

Of course, you could add a bunch of parentheses around, but that would be ugly and hard to read/edit as compared with dot notation.

Now, all of what I said above applies to symbolic method names too. However symbolic methods look unnatural when used with dot syntax, and so I prefer the infix syntax for them.
Source: https://stackoverflow.com/questions/102 ... t-notation

Re: if var in/contains comma-separated list/array

by jeeswg » 23 Sep 2018, 19:52

Interesting point. I was trying so hard to come up with a good idea, I even thought of using an operator together with a method on a string:
if var contains "abc,def,ghi".split(",")

Re: if var in/contains comma-separated list/array

by kczx3 » 23 Sep 2018, 19:39

I’d rather a method on the string and object prototypes like JavaScript than using a keyword anyways

Re: if var in/contains comma-separated list/array

by jeeswg » 23 Sep 2018, 19:21

- The point of this forum is to *talk about* *built-in* functionality.
- We don't have a consensus here, so no pull requests from me.
- Like with the command/function syntax 6-year debate, people here have complained but offered nothing, delaying a decision.
- FWIW I'd say: string = 1 item, array = multiple items, that's enough for a working operator. I await ideas on haystack arrays and delimiter-separated lists.

- Re. 'easily shared'. Ideally, basic functionality should be newbie-friendly. So, no duplicate function errors, or differing functions with the same name, or 10 versions of the same function.

Re: if var in/contains comma-separated list/array

by guest3456 » 23 Sep 2018, 18:15

jeeswg wrote: - I'd want something built-in
then there is nothing to talk about. either build it in yourself and submit a pullrequest, or wait for lexikos to build it in
jeeswg wrote:so that scripts can be easily shared. So no custom functions.
just include your custom function in the script and it can be easily shared.

Re: if var in/contains comma-separated list/array

by jeeswg » 23 Sep 2018, 10:45

- Thanks for your response.
- I'd want something built-in, so that scripts can be easily shared. So no custom functions.
- RegEx is slow. (Although is StrSplit slower? I'll do some benchmark tests.)
- RegEx requires character escaping.
- RegEx requires fiddly ^, $ and parenthesis adding.
- RegEx handles string comparisons only. The 'in' operator might support handling for numeric comparisons.
- So StrSplit is the best existing option.

- I would really appreciate if people think about this problem, and try to come up with any syntax suggestions. I do find this question a tricky one.
- Another idea would be something like: var match "X,)abc,def,ghi". That would check for an exact match, comma-delimited. A kind of home-grown simpler RegEx for C/E/S/X (contains/ends/starts/exact).

Re: if var in/contains comma-separated list/array

by guest3456 » 23 Sep 2018, 10:36

there are function and regex alternatives in this thread:

https://autohotkey.com/boards/viewtopic ... 37&t=23033

using a C/P suffix is awful

Re: if var in/contains comma-separated list/array

by jeeswg » 23 Sep 2018, 06:50

- This is the one important AHK v2 issue I've seen without an easy solution.
- These are my 2 best solutions at present, please be mature about any feedback.
- If you specify 1 needle for contains/in, it should be relatively easy to add a 2nd needle without rewriting everything.

Code: Select all

;IDEA 1: xxxC/xxxP operators

if (var contains StrSplit("abc,def,ghi", ",")) ;we need *SOMETHING* better than this
if (var contains "item1,item1ctd,item1ctd") ;1 item
if (var containsC "item1,item2,item3") ;3 items (no special ',,' handling)
if (var containsP "item1|item2|item3") ;3 items (no special '||' handling)

;contains, containsC, containsP
;in, inC, inP
;starts, startsC, startsP ;potentially
;ends, endsC, endsP ;potentially

;note: the xxxP operators are useful for needles that contain commas and for conversion to/from ~= (RegExMatch)

;==================================================

;IDEA 2: StrSplit comma/pipe unary operators

if (var contains StrSplit("abc,def,ghi", ",")) ;we need *SOMETHING* better than this
if (var contains "item1,item1ctd,item1ctd") ;1 item
if (var contains splitc "item1,item2,item3") ;3 items (no special ',,' handling)
if (var contains splitp "item1|item2|item3") ;3 items (no special '||' handling)

;note: perhaps a 'split' or 'splitx' operator would split every character (n-length string to n-length array)

;note: alternatively/additionally, a binary operator: "a,b,c" split "," [although this puts the operator after the string, where it's less visible, and easy to miss]
;[EDIT:] if (var contains "abc,def,ghi" split ",") [not great, less clear than StrSplit()]

Re: if var in/contains comma-separated list/array

by jeeswg » 26 May 2018, 23:12

- I had thought that a CSL function could have dual functionality: array to comma-separated list (string) and vice versa. ["a", "b", "c"] <-> "a,b,c"
- The same would apply to PSL.

Code: Select all

q:: ;testing ArrCSL / CSL functions
oArray := ["a", "b", "c", "d", "e"]
vText := CSL(oArray)
MsgBox, % vText ;a,b,c,d,e
oArray := CSL(vText)
oArray2 := ArrCSL(vText)
MsgBox, % oArray.3 ;c
MsgBox, % oArray2.3 ;c
return

ArrCSL(vText)
{
	return StrSplit(vText, ",")
}

CSL(vText)
{
	if !IsObject(vText)
		return StrSplit(vText, ",")
	oArray := vText
	Loop, % oArray.Length()
		vOutput .= (A_Index=1?"":",") oArray[A_Index]
	return vOutput
}
- One related point. I had been thinking about function design, regarding separate (or combined) functions that work differently for linear/associative arrays.
- I noticed that there are numerous 'Obj' functions but no 'Arr' functions, and thought that the 'Arr'/'Obj' distinction could be something that I might consider.
- I don't feel strongly on the matter.

Re: if var in/contains comma-separated list/array

by jeeswg » 03 May 2018, 10:47

Maybe ObjCSL and ObjPSL functions? [Or ArrCSL and ArrPSL.]

Code: Select all

if vItem contains ObjCSL("a,b,c,d,e")
if (vItem contains ObjCSL("a,b,c,d,e")) ;two-way compatible
if StrContains(vItem, ObjCSL("a,b,c,d,e"))
if StrContains(vItem, StrSplit("a,b,c,d,e", ","))

if vItem contains ObjPSL("a|b|c|d|e")
if (vItem contains ObjPSL("a|b|c|d|e")) ;two-way compatible
if StrContains(vItem, ObjPSL("a|b|c|d|e"))
if StrContains(vItem, StrSplit("a|b|c|d|e", "|"))

;note: where vNeedle is one item, a string
if vItem contains vNeedle
if (vItem contains vNeedle) ;two-way compatible
if StrContains(vItem, vNeedle)
if InStr(vItem, vNeedle)

if vItem in vNeedle
if (vItem in vNeedle) ;two-way compatible
if StrExact(vItem, vNeedle) ;note: depends on A_StringCaseSense
if !("" vItem <> vNeedle)

Re: if var in/contains comma-separated list/array

by jeeswg » 29 Mar 2018, 21:14

- I've come to a conclusion. I see 5 functions. 4 simple 2-parameter functions (Contains/Ends/Exact/Starts), and 1 super-function (Match).
- For the smaller functions the model would be:
StrXXX(vText, vNeedles)
instead of:
StrXXX(vText, vNeedles, vDelim, vCaseSen, vMatch)
- The number of parameters has been reduced to keep things simple. It's useful to know that only 2 parameters are ever used, otherwise you have to spend a lot of energy reading through the function call, to check anything, including the number of parameters used.
- For things like specifying a delimiter or case sensitivity, a StrMatch function could be used.
- The three removed parameters:
-> A delimeter other than a comma can be achieved by StrSplit, this is very rare. To specify literal text use [LiteralString]. There is no special handling for ',,'.
-> Specifying case sensitivity would be done via A_StringCaseSense, this is very rare.
-> I found that returning the text of the matching item was never really needed, however, if you pass an array to vNeedles, the functions return the index of the matching item, so you could get the matching item that way.

- Here are the 4 smaller functions, and they treat a string as a comma-separated list. If anyone doesn't like contains/in etc using a comma-separated list, they'd better come up with a spectacular, effective, practical and succinct alternative. Perhaps an operator that would convert a comma-separated list into a linear array, that would be an impressive precedent for AutoHotkey to set.
- I do not think that special pipe-separated list handling is as needed, but it's worth considering.

Code: Select all

;e.g. JEE_StrStarts(vText, "http") ;comma-separated list
;e.g. JEE_StrStarts(vText, "http,www") ;comma-separated list
;e.g. JEE_StrStarts(vText, [","]) ;array containing literals
;e.g. JEE_StrStarts(vText, [vNeedle]) ;array containing literals
;e.g. JEE_StrStarts(vText, [vNeedle1, vNeedle2]) ;array containing literals
;e.g. JEE_StrStarts(vText, ["http"]) ;array containing literals
;e.g. JEE_StrStarts(vText, ["http", "www"]) ;array containing literals
;e.g. JEE_StrStarts(vText, StrSplit("http|www", "|")) ;array containing literals

JEE_StrContains(ByRef vText, ByRef vNeedles)
{
	local vKey, vValue
	for vKey, vValue in (IsObject(vNeedles) ? vNeedles : StrSplit(vNeedles, ","))
		if InStr(vText, vValue)
			return vKey
	return 0
}

JEE_StrEnds(ByRef vText, ByRef vNeedles)
{
	local vKey, vLen := StrLen(vText), vValue
	for vKey, vValue in (IsObject(vNeedles) ? vNeedles : StrSplit(vNeedles, ","))
		if !("" SubStr(vText, vLen-StrLen(vValue)+1) != vValue)
			return vKey
	return 0
}

JEE_StrExact(ByRef vText, ByRef vNeedles)
{
	local vKey, vValue
	for vKey, vValue in (IsObject(vNeedles) ? vNeedles : StrSplit(vNeedles, ","))
		if !("" vText != vValue)
			return vKey
	return 0
}

JEE_StrStarts(ByRef vText, ByRef vNeedles)
{
	local vKey, vValue
	for vKey, vValue in (IsObject(vNeedles) ? vNeedles : StrSplit(vNeedles, ","))
		if !("" SubStr(vText, 1, StrLen(vValue)) != vValue)
			return vKey
	return 0
}

Re: if var in/contains comma-separated list/array

by nnnik » 23 Feb 2018, 10:57

jeeswg wrote:- Regarding:
v2-thoughts
https://autohotkey.com/v2/v2-thoughts.htm

>>> Re. an array on the left-hand side. Perhaps this, the following two lines would be equivalent:

Code: Select all

vCount := ([a,b] contains [c,d])
vCount := (a contains [c,d]) + (b contains [c,d])
- So if oArrayA contains oArrayB would be equivalent to 'if at least one of the items in oArrayA contains at least one of the items in oArrayB'.
- One use would be the reverse of 'contains', 'if abcde contains one of abc,def,ghi', versus, 'if abc within one of abcde,fghij,klmno'.
- I didn't especially feel the need for 'if var contains', to become 'if list contains', so I would welcome any other ideas.
- I did feel that 'starts'/'ends' would be useful, like RegExMatch, but faster, and no escape issues.
No:

Code: Select all

v := [a,b]
v contains [ [a,b], [d,e],[f,g] ] ;false
v contains [ v, [d,e],[f,g] ] ;true
thats the only possible interpretion for this code.


Comma seperated listzs are not an option that should ever be considered in a basic functionality like this due to:
  • compatability with other languages
  • having overhead ( needing to escape strings in even the most basic scenarios )
  • consistency and confusion with the incosistency of having it

Top