0

Given the following three objects, what is an efficient way to return the first object that contains a key-value pair?

var obj = {
  item1: {
    name: 'apple',
    color: 'red'
  },
  item2: {
    name: 'blueberry',
    color: 'blue'
  },
  item3: {
    name: 'cherry',
    color: 'red'
  }
};

var obj2 = {
  collection: [
    {
      item1: {
        name: 'apple',
        color: 'red'
      },
      item2: {
        name: 'blueberry',
        color: 'blue'
      },
      item3: {
        name: 'cherry',
        color: 'red'
      }
    }
  ]
};

var obj3 = {
  items: [
    {
      item1: {
        name: 'apple',
        color: 'red'
      }
    },
    {
      item2: {
        name: 'blueberry',
        color: 'blue'
      },
    },
    {
      item3: {
        name: 'cherry',
        color: 'red'
      }
    }
  ]
};

I would like to get the same results for the following three statements:

getObject(obj, 'color', 'red');
getObject(obj2, 'color', 'red');
getObject(obj3, 'color', 'red');

Output:

{
  name: 'apple',
  color: 'red'
}

Here's what I have so far, but I think it's missing a closure somewhere since it breaks when the function calls itself:

function getObject(arg, key, val) {
  if (typeof arg!=='object') return null;
  switch (Object.prototype.toString.call(arg)) {
    case '[object Array]':
      for (var i=0; i<arg.length; ++i) {
        getObject(arg[i], key, val);
      }
      break;
    case '[object Object]':
      for (var i in arg) {
        if (arg.hasOwnProperty(i)) {
          if (typeof arg[i]==='object') {
            getObject(arg[i], key, val);
          } else {
            if (i===key && arg[i]===val) {
              return arg;
            }
          }
        }
      }
      break;
  }
}
2

4 Answers 4

4

You could use this function, which will also search in more deeply nested data structures.

function getObject(obj, prop, value) {
    if (Object(obj) !== obj) return; // It is not an object
    if (obj[prop] === value) return obj; // Found it
    for (var key in obj) {
        var result = getObject(obj[key], prop, value);
        if (result) return result; // Found it
    }
}

var obj = {item1: {name: 'apple',color: 'red'},item2: {name: 'blueberry',color: 'blue'},item3: {name: 'cherry',color: 'red'}};
var obj2 = {collection: [{item1: {name: 'apple',color: 'red'},item2: {name: 'blueberry',color: 'blue'},item3: {name: 'cherry',color: 'red'}}]};
var obj3 = {items: [{item1: {name: 'apple',color: 'red'}},{item2: {name: 'blueberry',color: 'blue'},},{item3: {name: 'cherry',color: 'red'}}]};

console.log(getObject(obj, 'color', 'red'));
console.log(getObject(obj2, 'color', 'red'));
console.log(getObject(obj3, 'color', 'red'));

Sign up to request clarification or add additional context in comments.

2 Comments

I actually like your answer the most and ended up using it, but unfortunately I accepted another answer before seeing your solution. Apologies.
I guess better late than never. I found out you could actually unselect an accepted answer. Thanks again for this awesomely concise solution!
1

You could use Array#some for a short cut, if the object is found.

function getObject(object, key, value) {
    var result;
    if (!object || typeof object !== 'object') return;
    if (object[key] === value) return object;
    Object.values(object).some(v => result = getObject(v, key, value));
    return result;
}

var obj = { item1: { name: 'apple', color: 'red' }, item2: { name: 'blueberry', color: 'blue' }, item3: { name: 'cherry', color: 'red' } },
    obj2 = { collection: [{ item1: { name: 'apple', color: 'red' }, item2: { name: 'blueberry', color: 'blue' }, item3: { name: 'cherry', color: 'red' } }] },
    obj3 = { items: [{ item1: { name: 'apple', color: 'red' } }, { item2: { name: 'blueberry', color: 'blue' } }, { item3: { name: 'cherry', color: 'red' } }] };

console.log(getObject(obj, 'color', 'red'));
console.log(getObject(obj2, 'color', 'red'));
console.log(getObject(obj3, 'color', 'red'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

Your approach within getObject is OK, however, as a general approach, you should transform the array for each all. You can use the function map in order to prepare the array for the finding process.

The function find "finds" the object according to a specific predicate.

This is assuming the indexes are related to the keys -> index i==0 -> items1, and so on.

var obj = {item1: {name: 'apple',color: 'red'},item2: {name: 'blueberry',color: 'blue'},item3: {name: 'cherry',color: 'red'}};
var obj2 = {collection: [{item1: {name: 'apple',color: 'red'},item2: {name: 'blueberry',color: 'blue'},item3: {name: 'cherry',color: 'red'}}]};
var obj3 = {items: [{item1: {name: 'apple',color: 'red'}},{item2: {name: 'blueberry',color: 'blue'},},{item3: {name: 'cherry',color: 'red'}}]};

let getObject = (o, key, value) => o.find(obj => obj[key] === value);

console.log(getObject(Object.values(obj), 'color', 'red'));
console.log(getObject(obj2.collection.map((o, i) => o[`item${i+1}`]), 'color', 'red'));
console.log(getObject(obj3.items.map((o, i) => o[`item${i+1}`]), 'color', 'red'));
.as-console-wrapper { min-height: 100%; }

3 Comments

Thanks for this, @Ele. While I admire the brevity of the solution, I won't be able to use this right now because (1) the code base is standarized on ES5, and (2) other developers who use this helper function will not know to .map() when appropriate.
@thdoan no problem!
@thdoan, I just stumbled over this comment: code base is standardized on ES5. If ES5 is a requirement, then that should be clearly mentioned in the question.
0

You can call the function recursively to reach an object that hasn't any objects-members and filter it by key and value:

function getObject(obj, k, v) {
    for (var key in obj) {
        if (typeof obj[key] === 'object') {
            return getObject(obj[key], k, v);
        } else if (key === k && obj[key] === v) {
            return(obj);
        }
    }   
}

var obj = {
  item1: {
    name: 'apple',
    color: 'red'
  },
  item2: {
    name: 'blueberry',
    color: 'blue'
  },
  item3: {
    name: 'cherry',
    color: 'red'
  }
};

var obj2 = {
  collection: [
    {
      item1: {
        name: 'apple',
        color: 'red'
      },
      item2: {
        name: 'blueberry',
        color: 'blue'
      },
      item3: {
        name: 'cherry',
        color: 'red'
      }
    }
  ]
};

var obj3 = {
  items: [
    {
      item1: {
        name: 'apple',
        color: 'red'
      }
    },
    {
      item2: {
        name: 'blueberry',
        color: 'blue'
      },
    },
    {
      item3: {
        name: 'cherry',
        color: 'red'
      }
    }
  ]
};
console.log(getObject(obj, 'color', 'red'));
console.log(getObject(obj2, 'color', 'red'));
console.log(getObject(obj3, 'color', 'red'));

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.