Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sort_by/2 support #2491

Open
sergeysosivio opened this issue Oct 13, 2022 · 10 comments
Open

sort_by/2 support #2491

sergeysosivio opened this issue Oct 13, 2022 · 10 comments

Comments

@sergeysosivio
Copy link

sergeysosivio commented Oct 13, 2022

Feature Request

Problem

When you have a columns and want to sort by multiple columns in various orders - you will start chaining reverses

my jq sort_by is stable, but if i need to sort by descending - I have to do double reverse, which i dont like

sort_by(.person)                      # first sort in ascending order
| reverse | sort_by(.city) | reverse  # then sort_by in descending order

In order to keep both city descending and person ascending, (but by city) - I feel like I need double reverse, so this api does not looks good, in terms of performance for advanced sorting.

Do you know any workaround, not using double reverse?

Proposed solution

Is it possible to add sort_by/2, to be used as follows: sort_by(.person, false) | sort_by(.city, true) ?
Second parameter is boolean - isDesc, which means you want to sort in descending not in ascending order

  • false equals to sort_by/1
  • true equals to reverse|sort_by/1|reverse

Is there any chance, that it can it be supported like that?

Links

Stackoverflow question here, if something will change:
https://stackoverflow.com/questions/74058111/how-to-sort-by-in-descending-order-without-reverse-in-jq

@sergeysosivio
Copy link
Author

also, to note - this is contrary to this feature request:
#2467

@jbrains
Copy link
Contributor

jbrains commented Oct 13, 2022

I support this idea. Is there any reason that sort_by/2 would create a serious performance hotspot compared to sort_by/varargs? If not, then support this idea because I also like the transparency of sort_by(.last_name) | sort_by(.first_name) | ....

@stephenlthorn
Copy link

I support this idea as well

@shachardevops
Copy link

Me too

@kaydanowski
Copy link

I support proposed solution.

@seanlvjie
Copy link

it's good idea

@itchyny
Copy link
Contributor

itchyny commented Nov 25, 2022

Note that currently the sort_by filter allows to use multiple fields for comparison; the jq query sort_by(.person, .city) already works just like ORDER BY person ASC, city ASC in SQL syntax. The argument separator in jq is ;, so sort_by(.person; false) could be considered as ORDER BY person DESC, and sort_by(.person, .city; false, false) to be ORDER BY person DESC, city DESC. But I'm afraid that the syntax looks hard to understand (true means ASC or DESC?), and what is worse, many people will make mistake using sort_by(.person, false) which works oppositely against sort_by(.person; false).

@wader
Copy link
Member

wader commented Nov 25, 2022

Would it make sense to have a sort_by(map; cmp) variant that uses a lambda to map values and then lambda to compare two mapped values for order? so cmp would get [<a>,<b>] or {a: <a>, b: <b>} as input and return a number? but i guess that kind of variant would have even worse performance but more flexible

@seanlvjie
Copy link

sort_by(.person, .city; false, false)

could this way work in jq 1.6?

@pkoppstein
Copy link
Contributor

Here is a filter for sorting by multiple criteria, where the direction (ASCENDING or DESCENDING) can be specified separately for each criterion. To avoid confusion, I've presented it as multisort_by/2 rather than sort_by/2, though it is similar to the suggestion made by @seanlvjie.

# reverse-sort by f while retaining the ordering within groups defined by f
def reverse_by(f):
  group_by(f) | reverse | [.[][]];

# sort using multiple criteria.
# `criteria` and `$directions` should be arrays of the same length.
# If $directions[$i] is 1, then sort_by(criteria[$i]) is used, otherwise reverse_by(criteria[$i]) is used.
# The final ordering will ensure that criteria[$i] takes precedence over criteria[$i+1] for all relevant $i.
def multisort_by( criteria; $directions ):
  if $directions == [] then .
  elif $directions[-1] == 1 then sort_by(criteria[-1]) | multisort_by( criteria[:-1]; $directions[:-1] )
  else reverse_by(criteria[-1]) | multisort_by( criteria[:-1]; $directions[:-1] )
  end;

Example:

def data:
[{a:10,b:2}, {a:50,b:4},
 {a:50,b:2}, {a:50,b:5}];

data | multisort_by( [.a, .b]; [-1, 1] )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants