1

OBJECTIVE

Make a function that looks through a list (collection) and returns an array of all objects that have equivalent property values (source).


EXAMPLE #1

    function where(collection, source) {
      var arr = [];
      return arr;
    }

    where([
        { first: 'Romeo', last: 'Montague' }, 
        { first: 'Mercutio', last: null }, 
        { first: 'Tybalt', last: 'Capulet' }], 

        { last: 'Capulet' });

EXPECTED OUTPUT #1

[{ first: 'Tybalt', last: 'Capulet' }]

EXAMPLE #2

where(
    [{ 'a': 1 }, 
     { 'a': 1 },  
     { 'a': 1, 'b': 2 }], 

     { 'a': 1 }), 

EXPECTED OUTPUT #2

[{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }]

QUESTIONS

  1. Typically I include some Psuedo-code to highlight my thought process. However, I think I've fallen too deep into the rabbit hole. What is the best approach here? Should I flatten the objects into arrays? Is there an equivalent indexOf() for objects?
  2. I've heard you can use Object.keys() and .hasOwnProperty() to help accomplish this, but cannot comprehend how these two methods would work together to tackle this problem.
6
  • So your difficulty is in how you would retrieve the key(s) specified in source? Commented May 18, 2015 at 16:29
  • Lodash to the rescue! lodash.com/docs#filter Commented May 18, 2015 at 16:32
  • @TedHopp Yes - my difficulty is retrieving the nested keys & values in the array and then comparing them with another object array Commented May 18, 2015 at 16:45
  • @Amit unfortunately this is apart of a online learning course and I cannot import my own libraries :( Commented May 18, 2015 at 16:45
  • You might find the getOwnPropertyNames() function useful. Commented May 18, 2015 at 16:56

2 Answers 2

1

Since you can't use external libraries, here's a simple way to do what you need:

function where(collection, source) {
   var keys = Object.keys(source);

   return collection.filter(function (item) {
      return keys.every(function (key) {
         return source[key] == item[key];
      });
   });
}
Sign up to request clarification or add additional context in comments.

2 Comments

it won't work for nested objects. see this jsfiddle jsfiddle.net/jigardafda/54Ls1ewb/1
True. although that's not part of the requirement. The solution can be expanded for deep comparison but will be more complicated.
0

-> Solution 1: Using Lodash Where

For this kind of problems, you can use Lodash utility library.

Lodash already has where function which you need.

see the below example.

var out = _.where([{
    first: 'Romeo',
    last: 'Montague'
  }, {
    first: 'Mercutio',
    last: null
  }, {
    first: 'Tybalt',
    last: 'Capulet'
  }],

  {
    last: 'Capulet'
  });

document.getElementById('out').innerHTML = JSON.stringify(out);


var out2 = _.where(
  [{
    'a': 1
  }, {
    'a': 1
  }, {
    'a': 1,
    'b': 2
  }],

  {
    'a': 1
  });

document.getElementById('out2').innerHTML = JSON.stringify(out2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.8.0/lodash.js"></script>

<div id="out"></div>
<hr/>
<div id="out2"></div>


-> Solution 2: Custom Where Implementation

This implementation works with strings, single object , one level object and multilevel objects.

function typeOf(o) {
  return Object.prototype.toString.call(o).split(' ')[1].split(']')[0].toLowerCase();
}

function isUndefined(o) {
  return typeOf(o) === 'undefined';
}

function where(collection, source) {
  var checkArr = [];

  if (typeOf(collection) === 'object') {
    checkArr = [collection];
  } else if (typeOf(collection) === 'array') {
    checkArr = collection;
  }

  function isObjectSemiSame(obj, source, u) {
    return Object.keys(source).every(function(key) {
      if (isUndefined(obj) || isUndefined(obj[key])) {
        return;
      }
      if (typeOf(source[key]) === 'object' || typeOf(source[key]) === 'array') {
        return isObjectSemiSame(obj[key], source[key]);
      }
      return source[key] === obj[key];
    });
  }

  return checkArr.filter(function(item) {
    return isObjectSemiSame(item, source);
  });
}

Jasmine tests for testing where with strings, single object, one level object and multilevel objects.

function typeOf(o) {
  return Object.prototype.toString.call(o).split(' ')[1].split(']')[0].toLowerCase();
}

function isUndefined(o) {
  return typeOf(o) === 'undefined';
}

function where(collection, source) {
  var checkArr = [];

  if (typeOf(collection) === 'object') {
    checkArr = [collection];
  } else if (typeOf(collection) === 'array') {
    checkArr = collection;
  }

  function isObjectSemiSame(obj, source, u) {
    return Object.keys(source).every(function(key) {
      if (isUndefined(obj) || isUndefined(obj[key])) {
        return;
      }
      if (typeOf(source[key]) === 'object' || typeOf(source[key]) === 'array') {
        return isObjectSemiSame(obj[key], source[key]);
      }
      return source[key] === obj[key];
    });
  }

  return checkArr.filter(function(item) {
    return isObjectSemiSame(item, source);
  });
}

describe('where method', function() {

  it('testing with strings', function() {
    var collection = [
      "one",
      "two",
      "three"
    ];

    var collection = [
      "bamboo",
      "two",
      "bamboo",
      "link"
    ];

    expect(where(collection, "two")).toEqual(["two"]);
    expect(where(collection, "bamboo")).toEqual(["bamboo", "bamboo"]);
  });

  it('testing with one object', function() {
    var collection1 = {
      name: 'raju',
      age: 23,

    };

    var collection2 = {
      name: 'Friko',
      age: 36,

    };

    expect(where(collection1, {
      name: 'raju'
    })).toEqual([collection1]);
    expect(where(collection1, {
      name: 'Dump'
    })).toEqual([]);
    expect(where(collection2, {
      age: 36
    })).toEqual([collection2]);
    expect(where(collection2, {
      car: 'audi'
    })).toEqual([]);
  });

  it('testing with one level object', function() {
    var collection = [{
      name: 'jack',
      age: 25
    }, {
      name: 'missi',
      age: 23
    }, {
      name: 'reddy',
      age: 46
    }];

    var source1 = {
      name: 'reddy'
    };


    var source2 = {
      age: 25
    };

    expect(where(collection, source1)).toEqual([collection[2]]);
    expect(where(collection, source2)).toEqual([collection[0]]);
  });

  it('testing with multilevel object', function() {
    var collection = [{
      name: 'jack',
      age: 25,
      level1: {
        name: 'l1',
        level2: {
          name: 'l2',
          level3: {
            name: 'l3'
          }
        }
      }
    }, {
      name: 'missi',
      age: 23,
      level1: {
        name: 'l1'
      }
    }, {
      name: 'reddy',
      age: 46,
      feature: {
        flag: false
      }
    }];

    var source1 = {
      level1: {
        name: 'l1'
      }
    };

    var source2 = {
      level1: {
        name: 'l1',
        level2: {
          name: 'l2'
        }
      }
    };

    var source3 = {
      feature: {
        flag: false
      }
    };

    expect(where(collection, source1).length).toBe(2);
    expect(where(collection, source1)).toEqual(jasmine.arrayContaining([
      collection[0],
      collection[1]
    ]));
    expect(where(collection, source2)).toEqual([collection[0]]);
    expect(where(collection, source3)).toEqual([collection[2]]);
  });

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/jasmine-html.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.2.1/boot.min.js"></script>

UPDATE: added custom where implementation with jasmine tests

5 Comments

Unfortunately my question is apart of an online curriculum and I cannot import my own libraries :(
You can check implementation of where in lodash library. see github.com/lodash/lodash/blob/master/lodash.js#L7272
@jad-panda did you look at the link you provided? you're basically sending readers down the rabbit hole.
@jad-panda - I suppose you did a fine job (not planning to validate though :-), but notice what the original question was... your solution is probably an exterme overkill. When someone looks for that kind of complexity, it's much better to use an external library.
@Amit Solution is not overkill its only 30 lines code. remaining code is tests. only to validate where is working correctly.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.