zsh-users
 help / Atom feed
* Filtering array on index
@ 2018-10-25 11:29 Jesper Nygårds
  2018-10-25 18:17 ` Mikael Magnusson
  2018-10-25 20:21 ` Bart Schaefer
  0 siblings, 2 replies; 11+ 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] 11+ 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
  1 sibling, 2 replies; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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
  1 sibling, 1 reply; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ messages in thread

end of thread, back to index

Thread overview: 11+ 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

zsh-users

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

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