sacora
sacora

Reputation: 285

How to find an object within a JS collection by value

I am working with an API right now and I am using details[5].Value to target information in the following format:

details:
    "value":[
        { 
            "ID": "6",
            "Name": "Links",
            "Value": "URL"
        },
        { 
            "ID": "7",
            "Name": "Other",
            "Value": "URL"
        }
        etc
    ]

The problem is that the location inside of the JSON response is likely to change in the future, making my code obsolete and as the url has the potential to change as well, I cannot target that.

I want a way to target the value of url, mostly, because of this, by the value of the "Name" property. However, if I use something like

_.where(details, { Name: "Links" }).Value

It comes back as undefined. I am not sure if there would be another way to get to the information?

Upvotes: 0

Views: 1324

Answers (3)

mu is too short
mu is too short

Reputation: 434665

There are a couple points of confusion here.

_.where returns an array:

Looks through each value in the list, returning an array of all the values that contain all of the key-value pairs listed in properties.

so your _.where(details, obj).Value will (almost) always give you undefined because an array is unlikely to have a Value property. _.findWhere on the other hand does return a single value:

Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.

Secondly, your details appears to look like:

let details = {
    value: [
        { ID: '6', Name: 'Links', Value: 'URL' },
        { ID: '7', Name: 'Other', Value: 'URL' },
        ...
    ]
}

so you don't want to search details, you want to search details.value.

Putting them together:

_(details.value).findWhere({ Name: 'Links' }).Value

or

_.findWhere(details.value, { Name: 'Links' }).Value

You could use Array.prototype.find (or Array.prototype.filter if you're looking for all matches) and write your own callback but you already have Underscore available so why bother? Furthermore, Backbone collections have findWhere and where methods and there are advantages to matching Backbone's overall terminology.

Upvotes: 2

Ele
Ele

Reputation: 33726

An alternative is using the Javascript built-in function find to get a specific object within an array.

  • This alternative allows you to pass either an object or a string.
  • If the byThis parameter is an object, the whole set of key-values must match with the key-values of every object within the target array.
  • Otherwise, if byThis is a string every object will be treated as string to make the necessary comparison.

let details = {  "value": [{    "ID": "6",    "Name": "Links",    "Value": "URL"  }, {    "ID": "7",    "Name": "Other",    "Value": "URL"  }]};

let findBy = (array, byThis) => {
  return array.find(o => {
    if (typeof byThis === 'object') return Object.keys(byThis).every(k => o[k] === byThis[k]);
    else if (typeof byThis === 'string') return o.toString() ===  byThis;
  });
}

let found = findBy(details.value, {Name: "Links"});

console.log(found);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Take a look at this mini function. Let me know if there is something wrong

Update

This is the ES5 Version

function f(key, value, array){
    return array.value.filter(function(sub_array){
        return sub_array[key] == value;
    });
}

This is the ES6 Golfed Version

f=(k,v,a)=>a.value.filter(_=>_[k]==v)

//This is your JSON 
var details = {
  value: [
    {
      "ID": "6",
      "Name": "Links",
      "Value": "URL"
    },
    {
      "ID": "7",
      "Name": "Other",
      "Value": "URL"
    }
  ]
}

// Short code
f=(k,v,a)=>a.value.filter(_=>_[k]==v)

// f is the function name
// Recives k = array key, v = value, a = array
// filter by the given key and value
// return the result as an array

console.log(f('Name', 'Links', details))

Upvotes: 0

Related Questions