Skip to content

nth/2 semantic is strange #1867

@muhmuhten

Description

@muhmuhten

def nth(n; g): last(limit(n + 1; g)); does not match my intuition for what "the nth output of g" means when there are fewer than n+1 outputs of g.

Expected behavior

nth($n; exp) should probably be analogous to [exp][$n] (i.e. ([exp]|.[$n])), except less expensive and without evaluating the $n+1th and subsequent outputs of exp.

One thing to note is that $array[$n] != $array[:$n][-1], but more closely matches $array[$n:][0]... If $n is greater than the number of outputs, I'd expect to get back either empty or null. This implies something more like the following:

def drop($n; g): foreach g as $_ ($n; .-1; . < 0 or empty|$_);
def nth($n; g): first(drop($n; g), null);

Additional context

I thought I'd found a more efficient implementation of nth in the following, but well, the above:

diff --git a/src/builtin.jq b/src/builtin.jq
index a6cdabe..509047c 100644
--- a/src/builtin.jq
+++ b/src/builtin.jq
@@ -165,7 +165,9 @@ def any(condition): any(.[]; condition);
 def all: all(.[]; .);
 def any: any(.[]; .);
 def last(g): reduce . as $_ (.; g|[.]) | .[]?;
-def nth($n; g): if $n < 0 then error("nth doesn't support negative indices") else last(limit($n + 1; g)) end;
+def nth($n; g):
+  if $n < 0 then error("nth doesn't support negative indices")
+  else label $out | foreach g as $_ ($n; .-1; . < 0 or empty|$_, break $out) end;
 def first: .[0];
 def last: .[-1];
 def nth($n): .[$n];

This would be kind of a gratuitous incompatibility but might be nice for 2.0.
(The above first/drop implementation runs just as fast but reads nicer IMO.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions