admin管理员组

文章数量:1245133

I am working with a JSON object which can have a property ids at any leaf. I want to traverse the object and find all of the instances of the ids property and store each id in a collection.

Mocked up JSON Object (the ids property could be at much deeper property locations).

{
  "id": "b38a683d-3fb6-408f-9ef6-f4b853ed1193",
  "foo": {
    "ids": [
      {
        "id": "bd0bf3bd-d6b9-4706-bfcb-9c867e47b881"
      },
      {
        "id": "d1cc529d-d5d2-4460-b2bb-acf24a7c5999"
      },
      {
        "id": "b68d0c8c-548e-472f-9b01-f25d4b199a71"
      }
    ],
    "baz": "super"
  },
  "bar": {
    "ids": [
      {
        "id": "bd0bf3bd-d6b9-4706-bfcb-9c867e47b881"
      },
      {
        "id": "d1cc529d-d5d2-4460-b2bb-acf24a7c5999"
      },
      {
        "id": "b68d0c8c-548e-472f-9b01-f25d4b199a71"
      }
    ]
  }
}

I am using the following code to traverse the above JSON.

var jsonFile = require('./file_test.json'); // the above in my local directory

function traverse(obj, ids) {
  for (var prop in obj) {
    if (typeof obj[prop] == "object" && obj[prop]) {
      if (prop == 'ids') {
        for (var i = obj[prop].length - 1; i >= 0; i--) {
          ids.push(obj[prop][i]._id);
        };
      }
      traverse(obj[prop], ids);
    }
  }
}

var ids = new Array();
traverse(jsonFile, ids);

console.log('ids', ids);

The above nets the following:

ids
[
  'b68d0c8c-548e-472f-9b01-f25d4b199a71',
  'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
  'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881',
  'b68d0c8c-548e-472f-9b01-f25d4b199a71',
  'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
  'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881'
]

While my code works I am not convinced that I am doing this the most efficient or best way. Is there a better way to find all instances of the ids property? Perhaps without passing in an array but returning one? Or setting up for a callback with an ids array?

I am working with a JSON object which can have a property ids at any leaf. I want to traverse the object and find all of the instances of the ids property and store each id in a collection.

Mocked up JSON Object (the ids property could be at much deeper property locations).

{
  "id": "b38a683d-3fb6-408f-9ef6-f4b853ed1193",
  "foo": {
    "ids": [
      {
        "id": "bd0bf3bd-d6b9-4706-bfcb-9c867e47b881"
      },
      {
        "id": "d1cc529d-d5d2-4460-b2bb-acf24a7c5999"
      },
      {
        "id": "b68d0c8c-548e-472f-9b01-f25d4b199a71"
      }
    ],
    "baz": "super"
  },
  "bar": {
    "ids": [
      {
        "id": "bd0bf3bd-d6b9-4706-bfcb-9c867e47b881"
      },
      {
        "id": "d1cc529d-d5d2-4460-b2bb-acf24a7c5999"
      },
      {
        "id": "b68d0c8c-548e-472f-9b01-f25d4b199a71"
      }
    ]
  }
}

I am using the following code to traverse the above JSON.

var jsonFile = require('./file_test.json'); // the above in my local directory

function traverse(obj, ids) {
  for (var prop in obj) {
    if (typeof obj[prop] == "object" && obj[prop]) {
      if (prop == 'ids') {
        for (var i = obj[prop].length - 1; i >= 0; i--) {
          ids.push(obj[prop][i]._id);
        };
      }
      traverse(obj[prop], ids);
    }
  }
}

var ids = new Array();
traverse(jsonFile, ids);

console.log('ids', ids);

The above nets the following:

ids
[
  'b68d0c8c-548e-472f-9b01-f25d4b199a71',
  'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
  'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881',
  'b68d0c8c-548e-472f-9b01-f25d4b199a71',
  'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
  'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881'
]

While my code works I am not convinced that I am doing this the most efficient or best way. Is there a better way to find all instances of the ids property? Perhaps without passing in an array but returning one? Or setting up for a callback with an ids array?

Share Improve this question edited Aug 16, 2017 at 17:05 jherax 5,2675 gold badges39 silver badges50 bronze badges asked Apr 13, 2013 at 21:15 ahsteeleahsteele 26.5k28 gold badges138 silver badges252 bronze badges 0
Add a ment  | 

5 Answers 5

Reset to default 7

If the data was actually a JSON string, and not a JavaScript object, you could have something like:

// assuming `json` is the data string
var ids = [];
var data = JSON.parse(json, function(key, value) {
    if (key === "id") 
        ids.push(value);

    return value;
});

See reviver on JSON.parse method.

what you have is fine, but this is a little shorter and uses the .map function:

var jsonFile = require('./file_test.json'); // the above in my local directory
function traverse(obj) {
    var ids = [];
    for (var prop in obj) {
        if (typeof obj[prop] == "object" && obj[prop]) {
            if (prop == 'ids') {
                ids = obj[prop].map(function(elem){
                   return elem.id;
               })
            }
            ids =ids.concat(traverse(obj[prop]));
        }
    }
    return ids;
}

var ids =traverse(jsonFile);

console.log('ids', ids);

What you're basically trying to do is a tree search of this JSON object, am I right? So if we assume that ids is always a leaf then we do not need to traverse those nodes as we know they are at the leaf and will contain what we want.

  • Change the if {...} traverse to if {...} else {traverse}

If it is possible to change the data structure of ids to a list of strings instead of a list of objects then you will be able to save the iteration over the array and just merge it onto the ids array passed in, but it depends pletely on the context and whether or not you can make this change!

Sorry I'm not of more help!

Assuming ES5 is available natively or via a shim:

function gimmeIds(obj) {
    return Object.keys(obj||{})
        .reduce(function(ids, key) {
            if(key === 'ids') {
                return ids.concat(obj[key].map(function(idObj) {
                    return idObj.id;
                }));
            }

            if(obj[key] && typeof obj[key] == 'object') {
                return ids.concat(gimmeIds(obj[key]));
            }

            return ids;
        }, []);
}

Using object-scan this bees very simple. Note that you can easily specify what is targeted (in this case **.ids[*].id)

// const objectScan = require('object-scan');

const data = { id: 'b38a683d-3fb6-408f-9ef6-f4b853ed1193', foo: { ids: [{ id: 'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881' }, { id: 'd1cc529d-d5d2-4460-b2bb-acf24a7c5999' }, { id: 'b68d0c8c-548e-472f-9b01-f25d4b199a71' }], baz: 'super' }, bar: { ids: [{ id: 'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881' }, { id: 'd1cc529d-d5d2-4460-b2bb-acf24a7c5999' }, { id: 'b68d0c8c-548e-472f-9b01-f25d4b199a71' }] } };

const findIds = (input) => objectScan(['**.ids[*].id'], { rtn: 'value' })(input);

console.log(findIds(data));
/* => [ 'b68d0c8c-548e-472f-9b01-f25d4b199a71',
  'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
  'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881',
  'b68d0c8c-548e-472f-9b01-f25d4b199a71',
  'd1cc529d-d5d2-4460-b2bb-acf24a7c5999',
  'bd0bf3bd-d6b9-4706-bfcb-9c867e47b881' ]
 */
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>

Disclaimer: I'm the author of object-scan

本文标签: