1

Given a Javascript object, I wonder whether there is a way to use value to return the corresponding property? I understand properties have to be unique, but values can duplicate. So perhaps a way to use value to return the first matching property?

var x = { a: 1, b: 2, c: 3, d: 4, e: 4 };

Want to use value 4 to access property d.

UPDATE: Thanks for the helpful responses. I just realized my question may not be a good one, as object (from Javascript) or hash (from Ruby) is really an unordered list, so asking for the "first matching" is not quite sound.

wscourge
  • 10,657
  • 14
  • 59
  • 80

7 Answers7

2

So perhaps a way to use value to return the first matching property?

You're into new territory there: Object properties have only had a defined order since ES2015, and then only in relation to certain operations (a change in ES2020 makes the order relevant even to older operations that used to be exempt, in most cases).

In this case, since those properties don't fit the definition of an array index, and they're all "own" properties (not inherited), they'll be in order based on when they were created. In an object literal (like the one in your question), properties are created in source code order, and so the a property is the first and the e property is the last.

But again, this order is only applied by certain operations. Neither for-in nor Object.keys, for instance, is guaranteed to follow this order. (see above)

But Object.getOwnPropertyNames is. So to find the first matching property, we can use Object.getOwnPropertyNames to get an array, and then take the first property whose value matches the target (4):

function getFirstMatching(obj, value) {
  let result;
  Object.getOwnPropertyNames(obj).some(key => {
    if (obj[key] === value) {
      result = key;
      return true; // Stops the loop
    }
  });
  return result;
}
const x = {a: 1, b: 2, c: 3, d: 4, e: 4};
console.log(getFirstMatching(x, 4)); // d

Notice that I used some other ES2015 features in there (let, const, an arrow function). Since property order can't be shimmed/polyfilled, you can't rely on it in a non-ES2015 environment, so...

Please note the caveats on that:

  1. Requires a JavaScript engine that correctly supports ES2015 property order (cannot reliably be polyfilled/shimmed).
  2. Property order is only respected by certain operations (getOwnPropertynames is one of them).
  3. Property names that look like array indexes are not kept in creation order (they're kept in numeric order, prior to other properties).
  4. Inherited properties show up after own properties.

The caveats matter. For instance, here we get e, not d:

function getFirstMatching(obj, value) {
  let result;
  Object.getOwnPropertyNames(obj).some(key => {
    if (obj[key] === value) {
      result = key;
      return true; // Stops the loop
    }
  });
  return result;
}
// Note that now e is first
const x = {a: 1, b: 2, c: 3, e: 4, d: 4};
console.log(getFirstMatching(x, 4)); // e

Here we get q:

function getFirstMatching(obj, value) {
  var result;
  Object.getOwnPropertyNames(obj).some(key => {
    if (obj[key] === value) {
      result = key;
      return true; // Stops the loop
    }
  });
  return result;
}
// Note that now e is first
const x = {i: 1, j: 2, k: 3, q: 4};
const y = {a: 1, b: 2, c: 3, d: 4, e: 4};
for (let key in y) {
  x[key] = y[key];
}
console.log(getFirstMatching(x, 4)); // q
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Actually, OP wants first property with the value 4, so, i guess, loop should stop at it... :) – sinisake Dec 09 '16 at 17:16
  • @nevermind: Yeah, and they want the *first*, which radically changes the answer. Talk about not reading properly the first time. Bad T.J., bad! (Fixed the answer.) – T.J. Crowder Dec 09 '16 at 17:36
1

Try this:

function firstPropertyWithGivenValue(value, object){
   for(var key in object) {
     if(object[key] === value) return key;
   }
   return false;
}

firstPropertyWithGivenValue(4,x);
wscourge
  • 10,657
  • 14
  • 59
  • 80
  • 1
    No, that will not reliably (per spec) find the first one; that said, even though it's not specified, I don't know a JavaScript engine that wouldn't return `d` for that. – T.J. Crowder Dec 09 '16 at 17:36
0

One option is looping over the object. Use break to break the loop at first match.

var x = {a:1, b:2, c:3, d:4, e:4},
    match;
for(var i in x){
  if(x[i] == 4){
    match = i;
    break;
  }
}
console.log(match);
wscourge
  • 10,657
  • 14
  • 59
  • 80
Adam Azad
  • 11,171
  • 5
  • 29
  • 70
  • No, that will not reliably (per spec) find the first one; that said, even though it's not specified, I don't know a JavaScript engine that wouldn't return `d` for that. – T.J. Crowder Dec 09 '16 at 17:42
0

This is a slight modification of @wscourge's answer to ensure that the "first" property in lexicographic order is returned, without requiring ES2015.

It also allows customizing the "property order", if one should so desire.

function firstPropertyWithGivenValue(value, object) {
   var ret = undefined; 
   for (var key in object) {
       // To ignore inherited properties:
       // if (object.hasOwnProperty(key)) {
       if (object[key] == value) {
           if ((undefined === ret) || (key < ret)) {
               ret = key;
           }
       }
       // }
   }
   return ret;

}

This will return undefined if the property is not found, otherwise it will return the "first" property (remember that d comes before e, but e.g. key15 will come before key7, which might not be what one expects).

LSerni
  • 55,617
  • 10
  • 65
  • 107
0

You will have to loop over object to get the key whose value is matching.

Following is a generic function:

Logic:

  • Stringify the object and check if value exists in string. This will ensure you not looping if value is not there.
  • If exists, loop over keys of given object.
  • Check if current value is object. If yes, call self and search in the lower level. Since Object and Array both are of type object, you can use it as common logic.
  • If value is not an object, match it directly and return key.

Code

function searchValueInObj(obj, searchValue){
  if(!isAvailable(obj, searchValue)) return;
  
  for(var k in obj){
    if(!obj.hasOwnProperty(k)) continue;
    let o = obj[k];
    if(typeof(o) === "object" && isAvailable(o, searchValue)){
      return searchValueInObj(o, searchValue)
    }
    else if(o === searchValue) return k;
  }
}

function isAvailable(obj, searchVal){
  return JSON.stringify(obj).indexOf(searchVal) > -1
}

var x = {a:1, b:2, c:3, d:4, e:4, f: { g: 5, h:{i:6, j:{k: 7, l:4}}}};
console.log(searchValueInObj(x,4));
console.log(searchValueInObj(x,5));
console.log(searchValueInObj(x,7));
Community
  • 1
  • 1
Rajesh
  • 24,354
  • 5
  • 48
  • 79
-1
for (let item in x) {
    if (x[item] === 4){
        return item
    }
}

This will return d in your cause, but with a pretty major flaw, if the order of the object were to change, then you might end up with a different result.

var x = {a:4, b:2, c:3, d:4, e:4};

Using the above piece of code on this object would result in a being returned instead of d. So while it is possible, it's probably not best to find a property based on the value inside the object.

In what situation are you using this? There may be a more reliable way to get the result you're looking for.

Andrew Burns
  • 324
  • 4
  • 10
-2

var x = {a:1, b:2, c:3, d:4, e:4};
var re=4;
for(var a in x)
  {
if(x[a]==re){
console.log(a);
  break;
}
}
Mahi
  • 1,707
  • 11
  • 22