Skip to content

proposal: maps: Add Group and GroupFunc #66948

Open
@leb-kuchen

Description

@leb-kuchen

Proposal Details

I propose to add the functions Group and GroupFunc to the package maps , as I find myself reimplementing these often.
They work similarly to Object.GroupBy in JavaScript.

func Group[K comparable, V any](seq iter.Seq2[K, V]) map[K][]V {
	m := make(map[K][]V)
	for k, v := range seq {
		s := m[k]
		s = append(s, v)
		m[k] = s
	}
	return m
}
func GroupFunc[K comparable, V any](seq iter.Seq[V], f func(V) K) map[K][]V {
	m := make(map[K][]V)
	for v := range seq {
		k := f(v)
		s := m[k]
		s = append(s, v)
		m[k] = s
	}
	return m
}

Activity

added this to the Proposal milestone on Apr 21, 2024
moved this to Incoming in Proposalson Apr 21, 2024
adonovan

adonovan commented on Apr 22, 2024

@adonovan
Member

What's a typical implementation of Seq2 in this scenario? (Often a Seq2 is a map, which doesn't have repeated keys.)

leb-kuchen

leb-kuchen commented on Apr 22, 2024

@leb-kuchen
Author
type Person struct {
	Name   string
	Age    int
	Gender bool
}
func PersonAll(ps []Person) iter.Seq2[string, Person] {
	return func(yield func(string, Person) bool) {
		for _, p := range ps {
			if !yield(p.Name, p) {
				return
			}
		}
	}
}
adonovan

adonovan commented on Apr 22, 2024

@adonovan
Member

PersonAll is not an obvious iterator, because the first element (K=string) is redundant with the second (V=Person), and is not a primary key. Do we expect this kind of thing to be common?

leb-kuchen

leb-kuchen commented on Apr 22, 2024

@leb-kuchen
Author

Group is definitely less common than GroupFunc and this example can also be expressed with GroupFunc, but it has its use cases.

earthboundkid

earthboundkid commented on Apr 23, 2024

@earthboundkid
Contributor

Assume we have a people.All() iterator of some kind.

m := make(map[string][]Person)
for p := range people.All() {
    m[p.name] = append(m[p.name], p)
}

Would using a helper function save much here? Sometimes a loop is more clear than a helper function.

leb-kuchen

leb-kuchen commented on Apr 23, 2024

@leb-kuchen
Author

For simple use cases like this, I think GroupFunc is more idiomatic. If name was a getter, you would have to use a variable. This conveys its meaning more clearly.

GroupFunc(Values(ps), func(p Person) string {
	if p.Age < 20 {
		return "Youngster"
	}
	return "Adult"
})

Object.groupBy was originally intended to be Array.prototype.group and was only changed due web compatibly issues. So there seems to be a demand for this kind of function. The iterator also supports simple use cases like Limit and Filter. Of course you can say that a loop is idiomatic, but this could be said about every higher order function.

jimmyfrasche

jimmyfrasche commented on Aug 29, 2024

@jimmyfrasche
Member

A generalization of GroupBy is GroupReduce[K comparable, S, T any](sum S, reduce func(S, T) S, seq iter.Seq2[K, T]) map[K]S which makes GroupBy's implementation GroupReduce(nil, append, seq) (you need a wrapper around append to get it to work but I'm being a bit loose here to get the point across).

earthboundkid

earthboundkid commented on Aug 29, 2024

@earthboundkid
Contributor

Cross linking #69123, which is also about a grouping iterator.

jimmyfrasche

jimmyfrasche commented on Aug 29, 2024

@jimmyfrasche
Member

I'm not sure about GroupFunc. I see the utility but it seems to be fusing two operations.
If there were a

func KeyBy[K, V any](key func(V) K, seq iter.Seq[V]) iter.Seq2[K, V]

that yields (key(v), v) then GroupFunc would just be return GroupBy(KeyBy(key, seq)) but then again KeyBy is trivial to write in terms of Map21 #61898 (comment)

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @earthboundkid@jimmyfrasche@adonovan@gopherbot@leb-kuchen

        Issue actions

          proposal: maps: Add Group and GroupFunc · Issue #66948 · golang/go