jonplaca
jonplaca

Reputation: 827

Compare Object Arrays. Return matching Object keys & values

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.

Upvotes: 1

Views: 2027

Answers (2)

jad-panda
jad-panda

Reputation: 2547

-> 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

Upvotes: 0

Amit
Amit

Reputation: 46323

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];
      });
   });
}

Upvotes: 1

Related Questions