0

I am not sure if I am describing this problem well in my subject. I am not sure that 'location' is the proper way of describing the location of a parameter that is deeply nested within an object.

The following is a function that I have created that searches an array of objects and returns the index of the first object that has a property of a particular value.

function indexOfItemWithParam( items, location, value ) {

    var itemsIndex,
        directions = location.split( '.' ),
        directionsIndex,
        property;

    for( itemsIndex = 0; itemsIndex < items.length; itemsIndex++ ) {

        property = items[ itemsIndex ];

        for( directionsIndex = 0; directionsIndex < directions.length; directionsIndex++ ) {

            property = property[ directions[ directionsIndex ] ];

        }

        if( property == value ) {

            return itemsIndex;

        }

    }

    return false;

}

The special thing about this function is that if you have objects containing objects containing objects etc. you can describe the location of a property deep within the object hierarchy.

var people = [
    {
        id: 1,
        names: {
            first: 'John',
            last: 'Smith'
        },
        age: 45
    },
    {
        id: 2,
        names: {
            first: 'Jane',
            last: 'Jones'
        },
        age: 30
    },
];

console.log( indexOfItemWithParam( people, 'names.first', 'Jane' ) );

The console will return index position 1. The location of the first object with property names with property first of value Jane. As you can see I am passing a string mimics the the way an object is navigated in JavaScript, split it into an array using the full stop as a delimiter and then loop through each item to navigate through the objects.

This feels weird and like there should be a better way of doing this. I have not been able to find any other examples of functions trying to do a similar thing. Is this method, i.e. pass string, explode and loop, the best way to navigate nested objects?

McShaman
  • 3,627
  • 8
  • 33
  • 46

2 Answers2

1

Looks like you are looking for xpath like syntax for JSON (something like JSON Path).

Try goessner and this JSONPath.

gurvinder372
  • 66,980
  • 10
  • 72
  • 94
1

I have not been able to find any other examples of functions trying to do a similar thing.

Actually there are many such examples, have a look at Accessing nested JavaScript objects with string key or Convert JavaScript string in dot notation into an object reference.

Is this method, i.e. pass string, explode and loop, the best way to navigate nested objects?

No. Looping is quite necessary to access the nested properties, but you should not pass the path as a string. Let your function take the array right away. This is much more convenient and generic:

  • you don't have the responsibilty to (correctly) parse/explode strings
  • often enough, the caller is generic as well and already uses an array
  • no need to worry about edge cases (like array indices, symbol keys, or property names that contain dots)

function indexOfItemWithParam(items, path, value) {    
    for (var itemsIndex = 0; itemsIndex < items.length; itemsIndex++ ) {
        var property = path.reduce(function(property, direction) {
            return property[direction];
        }, items[itemsIndex]);
        if (property === value)
            return itemsIndex;
    }
    return -1;
}
// call as
indexOfItemWithParam(people, 'names.first'.split('.'), 'Jane') // or
indexOfItemWithParam(people, ['names', 'first'], 'Jane')

The most generic way to do this would be not to take a property path, but just a callback function that you can apply to get the "property" value to compare with. This is how the native array iteration methods (like reduce above) work as well. With e.g. findIndex you wouldn't need the helper method at all but could just call

people.findIndex(function(item) { return item.names.first === 'Jane'; })
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375