gleam-lang / stdlib

🎁 Gleam's standard library
https://hexdocs.pm/gleam_stdlib/
Apache License 2.0
466 stars 168 forks source link

Add `list.group_last` #669

Closed joshi-monster closed 1 month ago

joshi-monster commented 1 month ago

This is the utility function I reach for most often in other languages, after the basics like map.

It takes a list of elements and turns them into a lookup table using a key function. Similar functions in other libraries include _.keyBy, R.indexBy, or Dict.Extra.fromListBy.

joshi-monster commented 1 month ago

I've noticed that the list module doesn't use use, or dict.from_list. If that is a problem please let me know!

lpil commented 1 month ago

Hello! This seems an unusual function compared to group. Could you share some use cases please? 🙏

joshi-monster commented 1 month ago

As I said this comes up quite often for me (which might be a me problem) :slightly_smiling_face:

The main idea is that you can build up a simple lookup table to do in-memory joins:

these are all real-world examples, I have done them all in the past few months.


I personally think that group_first would be more useful, I chose group_last primarily because that is the behaviour of those other implementations, and the straight-forward imperative loop would also result in a group_last.

lpil commented 1 month ago

OK thank you.

I'm not entirely sure about the suitability here but perhaps others can weigh in.

I don't think the name is right as it doesn't return groups, it returns a single item for each key. What might we call this instead?

chuckwondo commented 1 month ago

How about to_dict?

joshi-monster commented 1 month ago

Hm, dict.from_list is indeed very similar to this, making this essentially an optimised version of map |> compose(from_list). I've had this a few times now that functions in the stdlib are just not in the place I look for them first :smile:


I don't like key_by or index_by because it might be confusing, so... maybe lookup_by? Otherwise, I think dict.from_list_by/with might also be a good place for something like this.

How would you feel about instead adding a more complex dict.from_list_with function that let you decide which element you wanted to keep? I still think a version that kept the first element would be really useful!

fn from_list_with(
    from list: List(val),
    by key: fn(val) -> key, // by pairs: fn(elem) -> #(key, val) // ??
    with fun: fn(key, val, val) -> val
) -> Dict(key, val)

There is also Ramdas reduceBy function which is a combined group and fold, but maybe at some point it's nicer to just write the loop yourself :smile:

lpil commented 1 month ago

I think using from_list is the way to go here. Thanks all!