Skip to content

Commit 4b9ebd1

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

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

fieldpath/set.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,29 @@ 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+
members := PathElementSet{}
213+
for _, member := range s.Members.members {
214+
isChild := false
215+
for _, child := range s.Children.members {
216+
if member.Equals(child.pathElement) {
217+
isChild = true
218+
}
219+
}
220+
if !isChild {
221+
// any members that are not also children are leaves
222+
members.members = append(members.members, member)
223+
}
224+
}
225+
226+
return &Set{
227+
Members: members,
228+
Children: *s.Children.Leaves(),
229+
}
230+
}
231+
209232
// setNode is a pair of PathElement / Set, for the purpose of expressing
210233
// nested set membership.
211234
type setNode struct {
@@ -455,3 +478,17 @@ func (s *SetNodeMap) iteratePrefix(prefix Path, f func(Path)) {
455478
n.set.iteratePrefix(append(prefix, pe), f)
456479
}
457480
}
481+
482+
// Leaves returns a SetNodeMap containing
483+
// only setNodes with leaf PathElements.
484+
func (s *SetNodeMap) Leaves() *SetNodeMap {
485+
out := &SetNodeMap{}
486+
for _, n := range s.members {
487+
next := setNode{
488+
pathElement: n.pathElement,
489+
set: n.set.Leaves(),
490+
}
491+
out.members = append(out.members, next)
492+
}
493+
return out
494+
}

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)