Skip to content

Commit 36ddca4

Browse files
committed
Add Leaves method to fieldpath.Set object
1 parent a4e00e9 commit 36ddca4

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

fieldpath/set.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,34 @@ func (s *Set) WithPrefix(pe PathElement) *Set {
206206
return subset
207207
}
208208

209+
func (s *Set) leavesPrefix(prefix Path, set *Set) {
210+
for _, child := range s.Children.members {
211+
child.set.leavesPrefix(append(prefix, child.pathElement), set)
212+
}
213+
214+
for _, mem := range s.Members.members {
215+
isChild := false
216+
currentPath := append(prefix, mem)
217+
for _, child := range s.Children.members {
218+
if mem.Equals(child.pathElement) {
219+
isChild = true
220+
}
221+
}
222+
if !isChild {
223+
// any members that are not also children are leaves
224+
set.Insert(currentPath)
225+
}
226+
}
227+
}
228+
229+
// Leaves returns a set containing only the leaf paths
230+
// of a set.
231+
func (s *Set) Leaves() *Set {
232+
out := &Set{}
233+
s.leavesPrefix(Path{}, out)
234+
return out
235+
}
236+
209237
// setNode is a pair of PathElement / Set, for the purpose of expressing
210238
// nested set membership.
211239
type setNode struct {

fieldpath/set_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ func BenchmarkFieldSet(b *testing.B) {
140140
randOperand().RecursiveDifference(randOperand())
141141
}
142142
})
143+
b.Run(fmt.Sprintf("leaves-%v", here.size), func(b *testing.B) {
144+
b.ReportAllocs()
145+
for i := 0; i < b.N; i++ {
146+
randOperand().Leaves()
147+
}
148+
})
143149
}
144150
}
145151

@@ -456,6 +462,106 @@ func TestSetIntersectionDifference(t *testing.T) {
456462
})
457463
}
458464

465+
func TestSetLeaves(t *testing.T) {
466+
table := []struct {
467+
name string
468+
input *Set
469+
expected *Set
470+
}{
471+
{
472+
name: "empty set",
473+
input: NewSet(),
474+
expected: NewSet(),
475+
}, {
476+
name: "all leaves",
477+
input: NewSet(
478+
_P("path1"),
479+
_P("path2"),
480+
_P("path3"),
481+
),
482+
expected: NewSet(
483+
_P("path1"),
484+
_P("path2"),
485+
_P("path3"),
486+
),
487+
}, {
488+
name: "only one leaf",
489+
input: NewSet(
490+
_P("root"),
491+
_P("root", "l1"),
492+
_P("root", "l1", "l2"),
493+
_P("root", "l1", "l2", "l3"),
494+
),
495+
expected: NewSet(
496+
_P("root", "l1", "l2", "l3"),
497+
),
498+
}, {
499+
name: "multiple values, check for overwrite",
500+
input: NewSet(
501+
_P("root", KeyByFields("name", "a")),
502+
_P("root", KeyByFields("name", "a"), "name"),
503+
_P("root", KeyByFields("name", "a"), "value", "b"),
504+
_P("root", KeyByFields("name", "a"), "value", "c"),
505+
),
506+
expected: NewSet(
507+
_P("root", KeyByFields("name", "a"), "name"),
508+
_P("root", KeyByFields("name", "a"), "value", "b"),
509+
_P("root", KeyByFields("name", "a"), "value", "c"),
510+
),
511+
}, {
512+
name: "multiple values and nested",
513+
input: NewSet(
514+
_P("root", KeyByFields("name", "a")),
515+
_P("root", KeyByFields("name", "a"), "name"),
516+
_P("root", KeyByFields("name", "a"), "value", "b"),
517+
_P("root", KeyByFields("name", "a"), "value", "b", "d"),
518+
_P("root", KeyByFields("name", "a"), "value", "c"),
519+
),
520+
expected: NewSet(
521+
_P("root", KeyByFields("name", "a"), "name"),
522+
_P("root", KeyByFields("name", "a"), "value", "b", "d"),
523+
_P("root", KeyByFields("name", "a"), "value", "c"),
524+
),
525+
}, {
526+
name: "all-in-one",
527+
input: NewSet(
528+
_P("root"),
529+
_P("root", KeyByFields("name", "a")),
530+
_P("root", KeyByFields("name", "a"), "name"),
531+
_P("root", KeyByFields("name", "a"), "value", "b"),
532+
_P("root", KeyByFields("name", "a"), "value", "b", "c"),
533+
_P("root", KeyByFields("name", "a"), "value", "d"),
534+
_P("root", KeyByFields("name", "a"), "value", "e"),
535+
_P("root", "x"),
536+
_P("root", "x", "y"),
537+
_P("root", "x", "z"),
538+
_P("root", KeyByFields("name", "p")),
539+
_P("root", KeyByFields("name", "p"), "name"),
540+
_P("root", KeyByFields("name", "p"), "value", "q"),
541+
),
542+
expected: NewSet(
543+
_P("root", KeyByFields("name", "a"), "name"),
544+
_P("root", KeyByFields("name", "a"), "value", "b", "c"),
545+
_P("root", KeyByFields("name", "a"), "value", "d"),
546+
_P("root", KeyByFields("name", "a"), "value", "e"),
547+
_P("root", "x", "y"),
548+
_P("root", "x", "z"),
549+
_P("root", KeyByFields("name", "p"), "name"),
550+
_P("root", KeyByFields("name", "p"), "value", "q"),
551+
),
552+
},
553+
}
554+
555+
for _, tt := range table {
556+
t.Run(tt.name, func(t *testing.T) {
557+
if got := tt.input.Leaves(); !tt.expected.Equals(got) {
558+
t.Errorf("expected %v, got %v for input %v", tt.expected, got, tt.input)
559+
}
560+
})
561+
}
562+
563+
}
564+
459565
func TestSetDifference(t *testing.T) {
460566
table := []struct {
461567
name string

0 commit comments

Comments
 (0)