Add Each method

Add Each method for iterating through all of the values in a document. This is in contrast to Walk which will iterate over a given path regardless of whether it exists or not.
This commit is contained in:
Tobie Morgan Hitchcock 2017-02-04 11:22:53 +00:00
parent 865a0b16a3
commit e6df3dbeb9
2 changed files with 85 additions and 10 deletions

View file

@ -751,14 +751,7 @@ func (d *Doc) Dec(value interface{}, path ...string) (*Doc, error) {
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
type walker func(key string, val interface{}) error type Iterator func(key string, val interface{}) error
// Walk walks the value or values at a specified path.
func (d *Doc) Walk(exec walker, path ...string) error {
return d.walk(exec, nil, path...)
}
func (d *Doc) join(parts ...[]string) string { func (d *Doc) join(parts ...[]string) string {
var path []string var path []string
@ -768,12 +761,73 @@ func (d *Doc) join(parts ...[]string) string {
return strings.Join(path, ".") return strings.Join(path, ".")
} }
func (d *Doc) walk(exec walker, prev []string, path ...string) error { // --------------------------------------------------------------------------------
// Each loops through the values in the data doc.
func (d *Doc) Each(exec Iterator) error {
return d.each(exec, nil)
}
func (d *Doc) each(exec Iterator, prev []string) error {
if d.data == nil {
return nil
}
// Define the temporary object so
// that we can loop over and traverse
// down the path parts of the data
object := d.data
// If the value found at the current
// path part is an object, then move
// to the next part of the path
if m, ok := object.(map[string]interface{}); ok {
for k, v := range m {
var keep []string
keep = append(keep, prev...)
keep = append(keep, k)
Consume(v).each(exec, keep)
}
return nil
}
// If the value found at the current
// path part is an array, then perform
// the query on the specified items
if a, ok := object.([]interface{}); ok {
for i, v := range a {
var keep []string
keep = append(keep, prev...)
keep = append(keep, fmt.Sprintf("%d", i))
Consume(v).each(exec, keep)
}
return nil
}
return exec(d.join(prev), object)
}
// --------------------------------------------------------------------------------
// Walk walks the value or values at a specified path.
func (d *Doc) Walk(exec Iterator, path ...string) error {
path = d.path(path...) path = d.path(path...)
return d.walk(exec, nil, path...)
}
func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
if len(path) == 0 { if len(path) == 0 {
d.data = nil
return nil return nil
} }

View file

@ -64,6 +64,16 @@ func TestOperations(t *testing.T) {
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
Convey("Can't each nil", t, func() {
doc := Consume(nil)
var i int
doc.Each(func(key string, val interface{}) error {
i++
return nil
})
So(i, ShouldEqual, 0)
})
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Ability to attempt new() // Ability to attempt new()
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -295,6 +305,17 @@ func TestOperations(t *testing.T) {
// ---------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------
Convey("Can iterate using each", t, func() {
var i int
doc.Each(func(key string, val interface{}) error {
i++
return nil
})
So(i, ShouldEqual, 15)
})
// ----------------------------------------------------------------------------------------------------
Convey("Does unset item exist", t, func() { Convey("Does unset item exist", t, func() {
So(doc.Exists("the.none"), ShouldBeFalse) So(doc.Exists("the.none"), ShouldBeFalse)
}) })