golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.98k stars 17.67k forks source link

Proposal: add generic Filter to slices package #58014

Closed karelbilek closed 1 year ago

karelbilek commented 1 year ago

A filter-like function would be useful in Slices package. It is not hard to write manually, but why not to have it in exp/slices (or even in built-in slices package).

This is a first, fizzbuzz-level code, draft, based on what I used locally; there might be a more optimised version.

func Filter[E any](s []E, f func(E) bool) []E {
    s2 := make([]E, 0, len(s))
    for _, e := range s {
        if f(e) {
             s2 = append(s2, e)
        }
    }
    return s2
}

(What I use locally is actually FilterWithError, with signature func FilterWithError[E any](s []E, f func(E) (bool, err)) ([]E, err)), but I think it would not fit into slices package as it is currently.)

When I asked on slack, it was recommended me to name it Select/Reject, to make more clear if filter is positive or negative. I think using "filter" is fine, as it is common in other languages and quite clear; Select means something else in Go so it could be confusing.

robpike commented 1 year ago

You can just use a for loop, which is more flexible in general. I am not being facetious; the filter operation tends to obscure allocation and overhead, and also tends to be overused. Although I may be in the minority, I do not believe this would be a wise addition to the standard library.

seankhliao commented 1 year ago

these were explicitly removed, see https://github.com/golang/go/discussions/47203#discussioncomment-1034432

karelbilek commented 1 year ago

@robpike That makes sense; we are having them in a business layer where the extra overhead is negligent compared with other tasks, but it's true it might be different on lower layers

@seankhliao ah ok. Thanks for pointing me in the right direction though.

DeedleFake commented 1 year ago

@4cq2

Go 1.21 adds slices.DeleteFunc(), which is essentially an in-place filter operation, so there's no need to copy and paste the internal implementation.

ghost commented 1 year ago

note the previous comment is misleading, as DeleteFunc does not return a copy, it edits the slice in place, which in some cases is not going to be what the user wants. here is actual filter that returns a copy:

https://github.com/golang/go/blob/go1.20.4/src/internal/types/testdata/check/slices.go

or, you could combine DeleteFunc with Clone:

https://pkg.go.dev/slices@master#Clone

also here is a function to invert a selection:

func Not[E any](fn func(E) bool) func(E) bool {
   return func(value E) bool {
      return !fn(value)
   }
}