简体   繁体   中英

Get array of key-values from array of objects without knowing format of array of objects (Javascript)?

Imagine I'm given a reference to an array of similar objects, eg array will be the name of that array. Now I'm asked to create an array of all the values of some property that is found inside each object of that array, eg "user.id" .

The problem is that I won't know the format of each object and where that property will reside/be nested.So "user.id" might reside in array[#].someKey ( array[#].someKey["user.id"] )or in array[#].someKey.someOtherKey ( array[#].someKey.someOtherKey["user.id"] )

Is there a function (jQuery, underscore..etc) that could create such an array ? eg var arrayOfUserIds = returnArray(array, "user.id");

For example, imagine that the following is an example of such an array :

var array = [
{
  "age": "13",
  "type": "publish_action",
  "tag": null,
  "timestamp": 1398931707000,
  "content": {
    "action": "publish",
    "user.id": "860",
    "user.email": "alex@somemail.com",
    "property.id": "2149",
    "iteration_id": "15427",
    "test_id": "6063",
    "property.name" : "bloop"
}, {
  ....
}, {
  ....
}];

Based on the above, I could obviously do :

var arrayOfUserIds = [];

for (var i=0; i<array.length; i++)
{
  arrayOfUserIds.push(array[i]["content"]["user.id"]);
}

But like I said, in my case I won't know about the format of the object so I can't create such a for-loop for example.

Any ideas will be much appreciated!

Thank you!

If I understand correctly, each object in someArray either contains a property user.id or an object that contains user.id ...or, recursively, some object that contains someArray . You want to create an array containing only the user.id properties.

An easy way to do this would be to do a recursive examination of each object in the array until user.id is located:

// get `user.id` property from an object, or sub-object
// it is assumed that there will only be one such property;
// if there are more than one, only the first one will be returned
function getUserId(o){
    if(o===null || o===undefined) return;
    if(o['user.id']) return o['user.id'];
    for(var p in o){
        if(!o.hasOwnProperty(p)) continue;
        if(typeof o[p] !== 'object') continue;
        if(o[p] === null || o[p] === undefined) continue;
        var id = getUserId(o[p]);
        if(id) return id;
    }
}

function getUserIds(arr){
    return arr.map(function(e){
        return getUserId(e);
    });
}

If you want something a little more generic, you could write a "find" method that will find all instances of a named property in an object tree:

 var find = (function(){
    function find(matches, o, prop, checkPrototypeChain){
        if(typeof o[prop] !== 'undefined') matches.push(o[prop]);
        for(var p in o){
            if(checkPrototypeChain || !o.hasOwnProperty(p)) continue;
            if(typeof o[p] !== 'object') continue;
            if(o[p] === null || o[p] === undefined) continue;
            find(matches, o[p], prop, checkPrototypeChain);
        }
    }
    return function(o, prop, checkPrototypeChain){
        var matches = [];
        find(matches, o, prop, checkPrototypeChain);
        return matches;
    }
})();

Then you could just map your array based on that:

var userIds = someArray.map(function(e){ return find(e, 'user.id'); });

Note that I'm glossing over properties that may be in the prototype chain, but in the find function, I added the ability to additionally search for properties in the prototype chain.

I went with the assumption that you're only working with primitives and object/array literals. In which case, the following method (using underscore) seems to do the trick.

var testSubject = {
    mykey: 9,
    firstArray: [
        {something: 9, another: {x: 'hello', mykey: 'dude'}, mykey: 'whatever'},
        {something: 9, another: {x: 'hello', mykey: 'dude2'}, mykey: 'whatever2'},
        {
            someArray: [
                {seven: 7, mykey: 'another'},
                {hasNo: 'mykey', atAll: 'mykey'}
            ]
        }
    ],
    anObject: {beef: 'jerky', mykey: 19}
};

function getValuesForKey(subject, searchKey) {
    return _.reduce(subject, function(memo, value, key) {
        if (_.isObject(value)) {
            memo = memo.concat(getValuesForKey(value, searchKey));
        } else if (key === searchKey) {
            memo.push(value);
        }
        return memo;
    }, []);
}

console.log(getValuesForKey(testSubject, 'mykey'));
// -> [9, "dude", "whatever", "dude2", "whatever2", "another", 19] 

It only returns the list of values because they will all share the same key (ie the one specified). Additionally, I do believe any matching keys will be ignored if their values are not primitive (eg mykey: {…} or mykey: […] should be ignored). I hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM