How to construct object in function using key passed as argument

Viewed 38

I frequently need to create reusable function that performs transformations for a given field of input, for example:

def keep_field_only(field):
  {field}
;

or

def count_by(field):
  group_by(field) |
  map(
    {
      field: .[0].field,
      count: length
    }
  )
;

While group_by works fine with key passed as an argument, using it to construct object (eg. to keep only key in the object) doesn't work.

I believe it can be always worked around using path/1, but in my experience it significantly complicates code.

Other workaround I used is copying field +{new_field: field} at beginning of function, deleting it in the end, but it doesn't look very efficient or readable either.

Is there a shorter and more readable way?

Update:

Sample input:

[
  {"type":1, "name": "foo"},
  {"type":1, "name": "bar"},
  {"type":2, "name": "joe"}
]

Preferred function invocation and expected results:

.[] | keep_field_only(.type):

  {"type": 1}
  {"type": 1}
  {"type": 2}

 count_by(.type):
 
 [
   {"type":1, "count": 2},
   {"type":2, "count": 1}
 ]
2 Answers

You can define pick/1 as below,

def pick(paths):
  . as $in
  | reduce path(paths) as $path (null;
    setpath($path; $in | getpath($path))
  );

and use it like so:

.[] | pick(.type)

Online demo

def count_by(paths; filter):
  group_by(paths | filter) | map(
    (.[0] | pick(paths)) + {count: length}
  );
def count_by(paths):
  count_by(paths; .);
count_by(.type)

Online demo

I don't think there's a shorter and more readable way.

As you say, you can use path/1 to define your keep_field_only and count_by, but it can be done in a very simple way:

def keep_field_only(field):
  (null | path(field)[0]) as $field
  | {($field): field} ;

def count_by(field):
  (null | path(field)[0]) as $field
  | group_by(field)
  | map(
    {
      ($field): .[0][$field],
      count: length
    }
  );

Of course this is only intended to work in examples like yours, e.g. with invocations like keep_field_only(.type) or count_by(.type). However, thanks to setpath, the same technique can be used in more complex cases.

Related