Deep picking using Underscore.JS

2019-07-28 16:40发布

问题:

I am trying to use underscoreJs to manipulate a JavaScript object and having problems doing so.

Here is my example

var data = {
  "label": "SomeName",
  "parent": [{
    "id": "parentId",
    "resources": [{
      "name": "ID1NAME",
      "calls": [
        "user_get", "user2_post", "user3_delete"
      ]
    }, {
      "name": "ID2",
      "calls": [
        "employee1_get", "employee2_delete", "employee3_update"
      ]
    }]
  }]
};
var res = _(data).chain().
    pluck('parent').
    flatten().
    findWhere(function(item){
     item === "user_get"
    }).
    value();
    
console.log(res);

Using an element which is a part of data.parent.calls[] (example : "user_get") I would like to extract its parent object, i.e. data.parent[0].

I tried above but always get undefined. I appreciate any help on this.

回答1:

One of the problems you're having is your use of _.pluck. If you execute _.pluck over an object, it'll go over the keys of the object trying to retrieve the property you specified as the second argument (in this case, 'parent'). 'label' is a string and 'parent' is an array so thus the array that you get as a result is [undefined, undefined]. The rest will then go wrong.

One solution could be as follows:

function findCallIndexInParent(call, parent) {
    return _.chain(parent)
            .pluck('resources')
            .flatten()
            .findIndex(function (obj) {
                return _.contains(obj.calls, call);
            })
            .value();
}

function findCall(call, data) {
    var parent = data.parent;
    return parent[findCallIndexInParent(call, parent)];
}

console.log(findCall('user_get', data));

findCall is just a convenient method that will pass the parent property of data to findCallIndexInParent (that will retrieve the index where call is) and return the desired object with the parent array.

Lodash (a fork of underscore) provides a method to get the property of an object that would have come really handy in here (sadly, underscore doesn't have it).

The explanation of findCallIndexInParent is as follows:

  1. Chain the parent list
  2. pluck the resources array
  3. As pluck maps, it returns a list of lists so a flatten is needed.
  4. Find the index of the element which calls contains call
  5. Return the value (the index) of the object that contains call within parent.

Here's the fiddle. Hope it helps.



回答2:

This would seem to do the trick.

function findByCall(data, call) {
  return _.find(data.parent, function(parent) {         //From data.parent list, find an item that
    return _.some(parent.resources, function(resource) {//has such parent.resource that it
      return _.includes(resource.calls, call);          //includes the searched resource.calls item
    });
  });
}

//Test

var data = {
  "label": "SomeName",
  "parent": [{
    "id": "parentId",
    "resources": [{
      "name": "ID1NAME",
      "calls": [
        "user_get", "user2_post", "user3_delete"
      ]
    }, {
      "name": "ID2",
      "calls": [
        "employee1_get", "employee2_delete", "employee3_update"
      ]
    }]
  }]
};

console.log(findByCall(data, 'user_get'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>



回答3:

If I understand correctly, you want to get the index of the element in the parent array which has any resource with the specified call.

data = {
  "label": "SomeName",
  "parent": [{
    "id": "parentId",
    "resources": [{
      "name": "ID1NAME",
      "calls": [
        "user_get", "user2_post", "user3_delete"
      ]
    }, {
      "name": "ID2",
      "calls": [
        "employee1_get", "employee2_delete", "employee3_update"
      ]
    }]
  }]
}

// find the index of a parent 
const index = _.findIndex(data.parent, parent => 
    // that has any (some) resources
    _.some(parent.resources, resource => 
        // that contains 'user_get' call in its calls list
        _.contains(resource.calls, 'user_get')
    )
)

console.log(index) // 0
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

If you want to find the actual parent object, use find instead of findIndex

If you want to find all parent objects matching this call, use filter instead of findIndex