Skip to content

Commit f541d63

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

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

fieldpath/set.go

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

209+
// Leaves returns a set containing only the leaf paths
210+
// of a set.
211+
func (s *Set) Leaves() *Set {
212+
leaves := PathElementSet{}
213+
im := 0
214+
ic := 0
215+
prevDiff := 0
216+
217+
// any members that are not also children are leaves
218+
for im < len(s.Members.members) {
219+
member := s.Members.members[im]
220+
if ic >= len(s.Children.members) {
221+
leaves.members = append(leaves.members, member)
222+
im++
223+
continue
224+
}
225+
226+
diff := member.Compare(s.Children.members[ic].pathElement)
227+
if diff-prevDiff != 0 {
228+
leaves.members = append(leaves.members, member)
229+
}
230+
prevDiff = diff
231+
232+
if diff < 0 {
233+
im++
234+
} else if diff > 0 {
235+
ic++
236+
} else {
237+
im++
238+
ic++
239+
}
240+
}
241+
242+
return &Set{
243+
Members: leaves,
244+
Children: *s.Children.Leaves(),
245+
}
246+
}
247+
209248
// setNode is a pair of PathElement / Set, for the purpose of expressing
210249
// nested set membership.
211250
type setNode struct {
@@ -455,3 +494,17 @@ func (s *SetNodeMap) iteratePrefix(prefix Path, f func(Path)) {
455494
n.set.iteratePrefix(append(prefix, pe), f)
456495
}
457496
}
497+
498+
// Leaves returns a SetNodeMap containing
499+
// only setNodes with leaf PathElements.
500+
func (s *SetNodeMap) Leaves() *SetNodeMap {
501+
out := &SetNodeMap{}
502+
out.members = make(sortedSetNode, len(s.members))
503+
for i, n := range s.members {
504+
out.members[i] = setNode{
505+
pathElement: n.pathElement,
506+
set: n.set.Leaves(),
507+
}
508+
}
509+
return out
510+
}

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)