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

slice: deprecate Dedup, shim to the equivalent stdlib slices.Compact #16

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 5 additions & 49 deletions slice/slice.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Package slice implements some useful functions for slices.
package slice

import "slices"

// Partition rearranges the elements of vs in-place so that all the elements v
// for which keep(v) is true precede all those for which it is false. It
// returns the prefix of vs that contains the kept elements. It takes time
Expand Down Expand Up @@ -69,57 +71,11 @@ func Partition[T any](vs []T, keep func(T) bool) []T {

// Dedup rearranges the elements of vs in-place to deduplicate consecutive runs
// of identical elements. It returns a prefix of vs that contains the first
// element of each run found, in their original relative order. It takes time
// proportional to len(vs) and does not allocate storage outside the slice.
// element of each run found.
//
// The returned slice will contain (non-consecutive) duplicates only if
// vs is not in sorted order at input. If vs is sorted at input (in either
// direction), the elements of the prefix are exactly the unique first elements
// of the input.
// Deprecated: Use the equivalent [slices.Compact] instead.
danderson marked this conversation as resolved.
Show resolved Hide resolved
func Dedup[T comparable](vs []T) []T {
if len(vs) <= 1 {
return vs
}

// Setup:
// i : the location of the first element of the next run
// j : runs forward from i looking for the end of the run
//
i, j := 0, 1
for {
// Scan forward from i for an element different from vs[i].
for j < len(vs) && vs[i] == vs[j] {
j++
}

// If there are no further distinct elements, we're done. The item at
// position i is the beginning of the last run in the slice.
if j == len(vs) {
return vs[:i+1]
}

// Reaching here, the slice looks like this:
//
// [a b c d d d d d d d e ? ? ?]
// 0 i j n
//
// where a, b, c, d are distinct from their neighbors.

// Otherwise, we have found the first item of a new run at j.
// Move i forward to the next slot and (if necessary) swap vs[j] into it.
i++
if j > i {
// A swap is unnecessary (though harmless) if j is already the next slot.
vs[i], vs[j] = vs[j], vs[i]
}
j++

// Now:
// swapped
// v-----------v
// [a b c d e d d d d d d ? ? ?]
// 0 i j n
}
return slices.Compact(vs)
}

// Reverse reverses the contents of vs in-place.
Expand Down
35 changes: 0 additions & 35 deletions slice/slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,6 @@ func TestPartition(t *testing.T) {
}
}

func TestDedup(t *testing.T) {
for _, test := range []testCase[int]{
{"Nil", nil, nil, nil},
{"Empty", []int{}, nil, nil},
{"One", []int{100}, []int{100}, nil},
{"NoRuns", []int{1, 3, 2, 4}, []int{1, 3, 2, 4}, nil},
{"Single", []int{5, 5, 5, 5, 5}, []int{5}, nil},
{"Two", []int{2, 2, 2, 3, 3, 3}, []int{2, 3}, nil},
{"Repeat", []int{1, 3, 3, 1}, []int{1, 3, 1}, nil},
{"NoRunsAsc", []int{0, 1, 2, 3, 4}, []int{0, 1, 2, 3, 4}, nil},
{"NoRunsDesc", []int{10, 9, 8, 7}, []int{10, 9, 8, 7}, nil},
{"RunsAsc", []int{0, 1, 1, 1, 2, 2, 3}, []int{0, 1, 2, 3}, nil},
{"RunsDesc", []int{5, 5, 5, 3, 3, 1, 1, 0}, []int{5, 3, 1, 0}, nil},

// Runs: a b--- c b--- d--- e---------
{"Unsorted", []int{1, 0, 0, 9, 0, 0, 3, 3, 2, 2, 2, 2}, []int{1, 0, 9, 0, 3, 2}, nil},
} {
t.Run(test.desc, test.dedup)
}
}

func TestReverse(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -542,20 +521,6 @@ func (tc *testCase[T]) partition(t *testing.T) {
}
}

func (tc *testCase[T]) dedup(t *testing.T) {
t.Helper()

cp := copyOf(tc.input)
t.Logf("Input: %+v", cp)

got := slice.Dedup(cp)
t.Logf("After dedup: %+v ~ %+v", got, cp[len(got):])
diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty())
if diff != "" {
t.Errorf("Dedup result (-want, +got)\n%s", diff)
}
}

func copyOf[T any](vs []T) []T {
out := make([]T, len(vs))
copy(out, vs)
Expand Down