zsh-users
 help / color / Atom feed
* Filtering array on index
@ 2018-10-25 11:29 Jesper Nygårds
  2018-10-25 18:17 ` Mikael Magnusson
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Jesper Nygårds @ 2018-10-25 11:29 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 909 bytes --]

I have an array which is the result of using zparseopts on a specification
that makes it possible to specify several filters with a -v flag. The
resulting array might look like the following:

myarr=(-v filter1 -v filter2)

I want to get rid of the "-v" elements, but only when they are the flag,
and not the argument. In other words, I would the like the array (-v
filter1 -v -v) to result in (filter1 -v).

I had previously used:

${myarr:#-v}}

which worked well, as I could accept the fact that -v was not possible to
use as the option argument.

However, now I need to make sure that a -v argument is preserved, and I
thus need a compact way of specifying that I want to keep every other
element in the array.

The most compact way I have found is:
for dash arg in $myarr; do myarr[(r)$dash]=()

This works, but I was wondering if there's some more elegant solution,
preferrably without using iteration?

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-25 11:29 Filtering array on index Jesper Nygårds
@ 2018-10-25 18:17 ` Mikael Magnusson
  2018-10-25 18:55   ` Jesper Nygårds
  2018-10-25 19:42   ` Daniel Shahaf
  2018-10-25 20:21 ` Bart Schaefer
  2019-07-23  3:38 ` Sebastian Gniazdowski
  2 siblings, 2 replies; 15+ messages in thread
From: Mikael Magnusson @ 2018-10-25 18:17 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

On 10/25/18, Jesper Nygårds <jesper.nygards@gmail.com> wrote:
> I have an array which is the result of using zparseopts on a specification
> that makes it possible to specify several filters with a -v flag. The
> resulting array might look like the following:
>
> myarr=(-v filter1 -v filter2)
>
> I want to get rid of the "-v" elements, but only when they are the flag,
> and not the argument. In other words, I would the like the array (-v
> filter1 -v -v) to result in (filter1 -v).
>
> I had previously used:
>
> ${myarr:#-v}}
>
> which worked well, as I could accept the fact that -v was not possible to
> use as the option argument.
>
> However, now I need to make sure that a -v argument is preserved, and I
> thus need a compact way of specifying that I want to keep every other
> element in the array.
>
> The most compact way I have found is:
> for dash arg in $myarr; do myarr[(r)$dash]=()
>
> This works, but I was wondering if there's some more elegant solution,
> preferrably without using iteration?

The above example doesn't work if you need to preserve the order (it
will just remove the first $dash it finds).

% a=(-v filter1 -v -v -v middle -v -v -v final)
% for dash arg in $a; do a[(r)$dash]=(); done; pl $a
filter1
middle
-v
-v
final

middle "should" be between the two remaining -v.

This works, but is still a loop obviously:
% for i in {$(($#a/2))..1}; do a[i*2-1]=(); done; pl $a
filter1
-v
middle
-v
final

(Note that it is necessary to iterate backwards as the indices will
change otherwise)

It is very easy to add -v back again however ;)
% a=( ${${:--v}:^^a} ); pl $a
-v
filter1
-v
-v
-v
middle
-v
-v
-v
final

-- 
Mikael Magnusson

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-25 18:17 ` Mikael Magnusson
@ 2018-10-25 18:55   ` Jesper Nygårds
  2018-10-25 19:42   ` Daniel Shahaf
  1 sibling, 0 replies; 15+ messages in thread
From: Jesper Nygårds @ 2018-10-25 18:55 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 426 bytes --]

Thank you. Nice examples. I hadn't thought about order, and in fact it's
not a problem in my use case.

I guess I'll have resign myself to using a loop. I was dreaming along the
lines of "a[e:<index is odd>:]=()", but no luck.

Although I understand the reason why zparseopts is designed this way, I've
always found it a bit cumbersome that there is no way to only get the
arguments to a possibly repeated flag into an array.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-25 18:17 ` Mikael Magnusson
  2018-10-25 18:55   ` Jesper Nygårds
@ 2018-10-25 19:42   ` Daniel Shahaf
  1 sibling, 0 replies; 15+ messages in thread
From: Daniel Shahaf @ 2018-10-25 19:42 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

Mikael Magnusson wrote on Thu, 25 Oct 2018 20:17 +0200:
> On 10/25/18, Jesper Nygårds <jesper.nygards@gmail.com> wrote:
> > I have an array which is the result of using zparseopts on a specification
> > that makes it possible to specify several filters with a -v flag. The
> > resulting array might look like the following:
> >
> > myarr=(-v filter1 -v filter2)
> >
> > I want to get rid of the "-v" elements, but only when they are the flag,
> > and not the argument. In other words, I would the like the array (-v
> > filter1 -v -v) to result in (filter1 -v).
> >
> > I had previously used:
> >
> > ${myarr:#-v}}
> >
> > which worked well, as I could accept the fact that -v was not possible to
> > use as the option argument.
> >
> > However, now I need to make sure that a -v argument is preserved, and I
> > thus need a compact way of specifying that I want to keep every other
> > element in the array.
> >
> > The most compact way I have found is:
> > for dash arg in $myarr; do myarr[(r)$dash]=()
> >
> > This works, but I was wondering if there's some more elegant solution,
> > preferrably without using iteration?
> 
> The above example doesn't work if you need to preserve the order (it
> will just remove the first $dash it finds).
> 
> % a=(-v filter1 -v -v -v middle -v -v -v final)
> % for dash arg in $a; do a[(r)$dash]=(); done; pl $a
> filter1
> middle
> -v
> -v
> final
> 
> middle "should" be between the two remaining -v.
> 
> This works, but is still a loop obviously:
> % for i in {$(($#a/2))..1}; do a[i*2-1]=(); done; pl $a
> filter1
> -v
> middle
> -v
> final
> 
> (Note that it is necessary to iterate backwards as the indices will
> change otherwise)

Another option:

myarr=(-v filter1 -v filter2)
() { local i j; myarr=(); for i j; do myarr+=($j) ; done } "${myarr[@]}"

In zparseopts context we can probably assume the arguments to the -v
option don't contain literal NUL bytes, so perhaps something like this:

% myarr=( -v 'foo foo' -v 'bar bar' -v '' )
% printf -v x '\0k1%s\0k2%s\0v' "$myarr[@]"
% x=${x//$'\0k1-v\0k2'}
% x=${x%$'\0v'}
% () { typeset -p argv } "${(@ps.\0v.)x}"
typeset -g -a argv=( 'foo foo' 'bar bar' '' )
% 

Is there a way to do this in O(N) time?  (The array-based solutions are
quadratic complexity due to indexing/appending being O(N).  I'm not sure
about the printf.)

Cheers,

Daniel

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-25 11:29 Filtering array on index Jesper Nygårds
  2018-10-25 18:17 ` Mikael Magnusson
@ 2018-10-25 20:21 ` Bart Schaefer
  2018-10-25 20:42   ` dana
  2019-07-23  3:38 ` Sebastian Gniazdowski
  2 siblings, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2018-10-25 20:21 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

On Thu, Oct 25, 2018 at 4:30 AM Jesper Nygårds <jesper.nygards@gmail.com> wrote:
>
> I have an array which is the result of using zparseopts on a specification
> that makes it possible to specify several filters with a -v flag. The
> resulting array might look like the following:
>
> myarr=(-v filter1 -v filter2)

What's your spec that produces this array?  Here's my stab at an equivalent:

% set -- -v -v one -v "two -v" -v three -v
% vees=()
% zparseopts -a vees v+::
% print -lr -- $vees
-v
-vone
-vtwo -v
-vthree
-v
% print -lr -- ${vees#-v}
one
two -v
three

What am I missing?

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-25 20:21 ` Bart Schaefer
@ 2018-10-25 20:42   ` dana
  2018-10-26  4:14     ` Jesper Nygårds
  0 siblings, 1 reply; 15+ messages in thread
From: dana @ 2018-10-25 20:42 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Jesper Nygårds, Zsh Users

On 25 Oct 2018, at 15:21, Bart Schaefer <schaefer@brasslantern.com> wrote:
>% zparseopts -a vees v+::
>...
>% print -lr -- ${vees#-v}

That method is the one i've seen used to solve this problem in completion
functions. But you have to make sure to add - at the end of the option spec if
you want it to work with mandatory (single-colon) arguments:

>An optional argument is put into the same array element as the option name
>(note that this makes empty strings as arguments indistinguishable). A
>mandatory argument is added as a separate element unless the ‘:-’ form is used,
>in which case the argument is put into the same element.

So if the argument to -v is mandatory you'd just use v+:- as the spec

dana


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-25 20:42   ` dana
@ 2018-10-26  4:14     ` Jesper Nygårds
  2018-10-26  7:59       ` Jesper Nygårds
  0 siblings, 1 reply; 15+ messages in thread
From: Jesper Nygårds @ 2018-10-26  4:14 UTC (permalink / raw)
  To: Zsh Users; +Cc: Bart Schaefer, dana

[-- Attachment #1: Type: text/plain, Size: 199 bytes --]

Thank you!

The combination of Bart's and Dana's answers gives me the perfect solution
for my problem. I had skipped over the documentation for "-" as part of the
spec, thinking it was not relevant.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-26  4:14     ` Jesper Nygårds
@ 2018-10-26  7:59       ` Jesper Nygårds
  2018-10-26  8:21         ` dana
  0 siblings, 1 reply; 15+ messages in thread
From: Jesper Nygårds @ 2018-10-26  7:59 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 1438 bytes --]

OK, one is never satisfied, as always. I have a continuation of this option
parsing business. And for the record, I realize I could solve this with a
loop, but it's always fun to see how far the impressive zsh functionality
can take one.

In addition to my filter option, I also have an override option which as it
name suggests is meant to be used as an override for the filters. In other
words, I want to remove the overrides from the list of filters. I'm looking
for a compact expression for this.

The following doesn't quite do what I want, since I can find no way of
specifying that I want the "-f" prefix removed from the elements in
$overrides, before its elements are then removed from the $filter list:

% filters=(); overrides=()
% set -- -v one -v two -f one
% zparseopts -D -E v+:-=filters f+:-=overrides
% print -lr -- ${${filters#-v}:|overrides}
one
two

(I wanted only "one" to be printed)

The documentation for ":|" explicitly says "arrayname is the name (N.B.,
not contents) of an array variable", so I can't do the usual trick of
nesting the operations. Is there a way to achieve this?


On Fri, Oct 26, 2018 at 6:14 AM Jesper Nygårds <jesper.nygards@gmail.com>
wrote:

> Thank you!
>
> The combination of Bart's and Dana's answers gives me the perfect solution
> for my problem. I had skipped over the documentation for "-" as part of the
> spec, thinking it was not relevant.
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-26  7:59       ` Jesper Nygårds
@ 2018-10-26  8:21         ` dana
  2018-10-26  9:28           ` Jesper Nygårds
  2018-10-28  2:45           ` Bart Schaefer
  0 siblings, 2 replies; 15+ messages in thread
From: dana @ 2018-10-26  8:21 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

On 26 Oct 2018, at 02:59, Jesper Nygårds <jesper.nygards@gmail.com> wrote:
>The following doesn't quite do what I want, since I can find no way of
>specifying that I want the "-f" prefix removed from the elements in
>$overrides, before its elements are then removed from the $filter list:
>
>% filters=(); overrides=()
>% set -- -v one -v two -f one
>% zparseopts -D -E v+:-=filters f+:-=overrides
>% print -lr -- ${${filters#-v}:|overrides}
>one
>two
>
>(I wanted only "one" to be printed)

If the idea is to remove the overrides from the filters, shouldn't you have
wanted only 'two' printed?

If so, i guess this might qualify as 'compact':

  ${filters:#${~:-(${(j<|>)${(b)overrides/#-f/-v}})}}

Add (M) to the outer expansion if you want it the other way around

And if you still need the -v removed when you're done, just wrap the whole thing
in ${...#-v}

dana


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-26  8:21         ` dana
@ 2018-10-26  9:28           ` Jesper Nygårds
  2018-10-28  2:45           ` Bart Schaefer
  1 sibling, 0 replies; 15+ messages in thread
From: Jesper Nygårds @ 2018-10-26  9:28 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 376 bytes --]

On Fri, Oct 26, 2018 at 10:21 AM dana <dana@dana.is> wrote:

> If the idea is to remove the overrides from the filters, shouldn't you have
> wanted only 'two' printed?
>

Ah yes, I screwed up the comment, you interpreted what I wanted correctly.
Thank you for the solution. Now, I'll have to think about where exactly the
boundary between "compact" and "convoluted" goes. :-)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-26  8:21         ` dana
  2018-10-26  9:28           ` Jesper Nygårds
@ 2018-10-28  2:45           ` Bart Schaefer
  1 sibling, 0 replies; 15+ messages in thread
From: Bart Schaefer @ 2018-10-28  2:45 UTC (permalink / raw)
  To: dana; +Cc: Jesper Nygårds, Zsh Users

On Fri, Oct 26, 2018 at 1:22 AM dana <dana@dana.is> wrote:
>
> If so, i guess this might qualify as 'compact':
>
>   ${filters:#${~:-(${(j<|>)${(b)overrides/#-f/-v}})}}

It's not even as difficult as that if you don't need to keep the "-v" prefixes:

% print -lr -- ${${filters#-v}:#${(j:|:b)~overrides#-f}}
two

If there are no filters that are a proper suffix of another filter,
you can do this to preserve the "-v" prefix:

% print -lr -- ${${filters}%${(j:|:b)~overrides#-f}}

> Add (M) to the outer expansion if you want it the other way around

Same above.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2018-10-25 11:29 Filtering array on index Jesper Nygårds
  2018-10-25 18:17 ` Mikael Magnusson
  2018-10-25 20:21 ` Bart Schaefer
@ 2019-07-23  3:38 ` Sebastian Gniazdowski
  2019-07-23  7:42   ` Sebastian Gniazdowski
  2 siblings, 1 reply; 15+ messages in thread
From: Sebastian Gniazdowski @ 2019-07-23  3:38 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

On Thu, 25 Oct 2018 at 13:30, Jesper Nygårds <jesper.nygards@gmail.com> wrote:
>
> I have an array which is the result of using zparseopts on a specification
> that makes it possible to specify several filters with a -v flag. The
> resulting array might look like the following:
>
> myarr=(-v filter1 -v filter2)

I've recently had this problem. One other way to do it:

~ idx=0; olen=$#a+1; a=( ${a[@]/(#m)*/$a[$(( ++idx % 2 ? olen : idx ))]} )
~ print -rl -- $a
filter1
filter2

-- 
Sebastian Gniazdowski
News: https://twitter.com/ZdharmaI
IRC: https://kiwiirc.com/client/chat.freenode.net:+6697/#zplugin
Blog: http://zdharma.org

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2019-07-23  3:38 ` Sebastian Gniazdowski
@ 2019-07-23  7:42   ` Sebastian Gniazdowski
  2019-07-23  8:03     ` Sebastian Gniazdowski
  2019-09-08  8:59     ` Sebastian Gniazdowski
  0 siblings, 2 replies; 15+ messages in thread
From: Sebastian Gniazdowski @ 2019-07-23  7:42 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

On Tue, 23 Jul 2019 at 05:38, Sebastian Gniazdowski
<sgniazdowski@gmail.com> wrote:
>
> On Thu, 25 Oct 2018 at 13:30, Jesper Nygårds <jesper.nygards@gmail.com> wrote:
> > myarr=(-v filter1 -v filter2)
>
> I've recently had this problem. One other way to do it:
>
> ~ idx=0; olen=$#a+1; a=( ${a[@]/(#m)*/$a[$(( ++idx % 2 ? olen : idx ))]} )

Turns out the expression can be simpler because the math mode within
array's index is implicit, so:

  idx=0; olen=$#a+1; a=( ${a[@]/(#m)*/$a[ ++idx % 2 ? olen : idx ]} )

I've also tried to construct an ::= assignment version. A safe.
different output variable version is:
  b=(); idx=0; olen=$#a+1; : ${a[@]/(#m)*/${b[++idx]::=${a[idx % 2 ?
olen : idx]}}}

However, this yields 8 empty elements (for the a=(-v filter1 -v filter2 ))::

% print -rl -- "${(q)b[@]}"
''
''
...

prepending $idx after ::= yields following output:
''
1
''
3
''
5
''
7

I wonder what's going on? The code looks OK. Maybe it's a bug?

-- 
Sebastian Gniazdowski
News: https://twitter.com/ZdharmaI
IRC: https://kiwiirc.com/client/chat.freenode.net:+6697/#zplugin
Blog: http://zdharma.org

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2019-07-23  7:42   ` Sebastian Gniazdowski
@ 2019-07-23  8:03     ` Sebastian Gniazdowski
  2019-09-08  8:59     ` Sebastian Gniazdowski
  1 sibling, 0 replies; 15+ messages in thread
From: Sebastian Gniazdowski @ 2019-07-23  8:03 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

On Tue, 23 Jul 2019 at 09:42, Sebastian Gniazdowski
<sgniazdowski@gmail.com> wrote:
> I wonder what's going on? The code looks OK. Maybe it's a bug?

So it seems that the ++idx within the ::=-treated array brackets is
executed twice. This might get considered a bug, but there's a chance
that this is how the code is arranged. After moving ++idx outside, the
code works:

% a=(-v filter1 -v filter2 )
% idx=0; olen=$#a+1; : ${a[@]/(#m)*/$((++idx))${a[idx]::=${a[idx % 2 ?
olen : idx]}}}
% print $a
filter1
filter2

Of course the empty elements are still there so an empty-element
eradicating use like above is needed.
-- 
Sebastian Gniazdowski
News: https://twitter.com/ZdharmaI
IRC: https://kiwiirc.com/client/chat.freenode.net:+6697/#zplugin
Blog: http://zdharma.org

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Filtering array on index
  2019-07-23  7:42   ` Sebastian Gniazdowski
  2019-07-23  8:03     ` Sebastian Gniazdowski
@ 2019-09-08  8:59     ` Sebastian Gniazdowski
  1 sibling, 0 replies; 15+ messages in thread
From: Sebastian Gniazdowski @ 2019-09-08  8:59 UTC (permalink / raw)
  To: Jesper Nygårds; +Cc: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 434 bytes --]

23 July 2019, 09:42 Sebastian Gniazdowski <sgniazdowski@gmail.com> wrote:

> Turns out the expression can be simpler because the math mode within
> array's index is implicit, so:
>
>   idx=0; olen=$#a+1; a=( ${a[@]/(#m)*/$a[ ++idx % 2 ? olen : idx ]} )
>

The expression can be even simpler if (valid in Zsh, just resolving to
empty element when no ksharrays) index 0 will be used:

idx=0; a=( ${a[@]/(#m)*/$a[ ++idx%2 ? 0 : idx ]} )

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, back to index

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-25 11:29 Filtering array on index Jesper Nygårds
2018-10-25 18:17 ` Mikael Magnusson
2018-10-25 18:55   ` Jesper Nygårds
2018-10-25 19:42   ` Daniel Shahaf
2018-10-25 20:21 ` Bart Schaefer
2018-10-25 20:42   ` dana
2018-10-26  4:14     ` Jesper Nygårds
2018-10-26  7:59       ` Jesper Nygårds
2018-10-26  8:21         ` dana
2018-10-26  9:28           ` Jesper Nygårds
2018-10-28  2:45           ` Bart Schaefer
2019-07-23  3:38 ` Sebastian Gniazdowski
2019-07-23  7:42   ` Sebastian Gniazdowski
2019-07-23  8:03     ` Sebastian Gniazdowski
2019-09-08  8:59     ` Sebastian Gniazdowski

zsh-users

Archives are clonable: git clone --mirror http://inbox.vuxu.org/zsh-users

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://inbox.vuxu.org/vuxu.archive.zsh.users


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git