-2

I have a list of users and the contents they have received already.

users = [
    { name: 'Steve', received_contents: ['1a', '1b', '3c'] },
    { name: 'Virginie', received_contents: ['3a', '2b', '3c'] },
    { name: 'Fiona', received_contents: ['1b', '2a', '3b'] },
    { name: 'Jenny', received_contents: ['3b', '2c', '1b'] },
    { name: 'James', received_contents: ['2b', '1b', '3a'] },
    { name: 'Fede', received_contents: ['2c', '3a', '1c'] },
    { name: 'Sara', received_contents: ['3a', '2c', '3b'] },
    { name: 'Tizi', received_contents: ['2b', '1b', '2a'] },
    { name: 'Thomas', received_contents: ['3c', '2b', '1a'] },
]

// These are the boxes for the next shipment and their contents

boxes = [
{ code: 'gr1', contents: ['1a', '1b'] },
{ code: 'gr2', contents: ['1a', '2b'] },
{ code: 'gr3', contents: ['1b', '2c'] },
{ code: 'gr4', contents: ['2c', '3c'] },
{ code: 'gr5', contents: ['3b', '1c'] },
]

The task is to create a function that accepts the list of users and returns a list of users and the boxes they can receive without getting the same contents again.

I'm a bit stuck as to how to make my solution more effective and time efficient.

Here's my solution:

    for (var i in users ){
        let user = users[i];
        console.log("User "+user.name+ " can receive " + getReceivableBoxes(user.received_contents));
    }

    function getReceivableBoxes (contents){
        let receivableBoxes = [];
        for(var i in boxes){
            let box = boxes[i];
            let canReceive = canReceiveBox(contents, box);
            if(canReceive){
                receivableBoxes.push(box.code);

            }

        }

        return receivableBoxes;
    }

    function canReceiveBox(received_contents, box) {
        let receivableBoxes = [];

        for (var i = 0; i < received_contents.length; i++) {
            for (var j = 0; j < box.contents.length; j++) {
                if (box.contents[j] === received_contents[i]) {
                    return false;
                }
            }
        }
        return true;
    }
rory-h
  • 660
  • 1
  • 12
  • 30
  • 2
    Possible duplicate of [How to get the difference between two arrays of objects in JavaScript](https://stackoverflow.com/questions/21987909/how-to-get-the-difference-between-two-arrays-of-objects-in-javascript) – Dexygen Jul 29 '18 at 22:00
  • @GeorgeJempty that question has a different type of array. I gave my solution for others to check. – rory-h Jul 29 '18 at 22:39

2 Answers2

0

Performance

JS

function getReceivableBoxes(user){
  const receivableBoxes = [];

  for(let b in boxes){
    const box = boxes[b];
    const contents = box.contents;
    const filtered = [];

    for(var i = 0, k = contents.length; i < k; i++){
      if(user.received_contents.indexOf(contents[i]) > -1){
        break;
      }

      filtered.push(contents[i]);
    }

    if(filtered.length === contents.length){
      receivableBoxes.push(box.code);
    }
  }

  return receivableBoxes;
}

Compact version

JS

users.forEach(function(user){
  var receivableBoxes = [];

  boxes.forEach(function(box){ 
    var contents = box.contents.filter(function(item){ 
      return !user.received_contents.includes(item);
    });

    if(contents.length === box.contents.length){
      receivableBoxes.push(box.code);
    }
  });

  console.log('@@@ User ' + user.name + ' can receive ' + receivableBoxes.join(','));
});

ES6

const users = [
    { name: 'Steve', received_contents: ['1a', '1b', '3c'] },
    { name: 'Virginie', received_contents: ['3a', '2b', '3c'] },
    { name: 'Fiona', received_contents: ['1b', '2a', '3b'] },
    { name: 'Jenny', received_contents: ['3b', '2c', '1b'] },
    { name: 'James', received_contents: ['2b', '1b', '3a'] },
    { name: 'Fede', received_contents: ['2c', '3a', '1c'] },
    { name: 'Sara', received_contents: ['3a', '2c', '3b'] },
    { name: 'Tizi', received_contents: ['2b', '1b', '2a'] },
    { name: 'Thomas', received_contents: ['3c', '2b', '1a'] }
]

const boxes = [
  { code: 'gr1', contents: ['1a', '1b'] },
  { code: 'gr2', contents: ['1a', '2b'] },
  { code: 'gr3', contents: ['1b', '2c'] },
  { code: 'gr4', contents: ['2c', '3c'] },
  { code: 'gr5', contents: ['3b', '1c'] }
];

users.forEach((user) => {
  const receivableBoxes = [];

  boxes.forEach((box) => { 
    const contents = box.contents.filter((item) => !user.received_contents.includes(item));

    if(contents.length === box.contents.length){
      receivableBoxes.push(box.code);
    }
  });
      
  console.log(`@@@ User ${user.name} can receive ${receivableBoxes.join(',')}`);
});

DEMO

dysfunc
  • 2,008
  • 1
  • 13
  • 15
  • are you sure that the first block will be executed faster than the es6 way? – messerbill Jul 30 '18 at 00:05
  • Yes. The for loop will execute faster than the forEach as it doesn’t execute a callback and the indexOf is faster than includes. – dysfunc Jul 30 '18 at 00:08
0

You can use ES6 array functions, map, filter, find and every. These functions are highly optimised by the developers of modern JavaScript engines. Timing it it usually executes in 0 or 1ms, the same time the answer above's code takes to run.

I would also like to point out that if you reached for a for loop or a forEach in a technical test for a modern JavaScript interview it would blow the interview for you.

const users = [
    { name: 'Steve', received_contents: ['1a', '1b', '3c'] },
    { name: 'Virginie', received_contents: ['3a', '2b', '3c'] },
    { name: 'Fiona', received_contents: ['1b', '2a', '3b'] },
    { name: 'Jenny', received_contents: ['3b', '2c', '1b'] },
    { name: 'James', received_contents: ['2b', '1b', '3a'] },
    { name: 'Fede', received_contents: ['2c', '3a', '1c'] },
    { name: 'Sara', received_contents: ['3a', '2c', '3b'] },
    { name: 'Tizi', received_contents: ['2b', '1b', '2a'] },
    { name: 'Thomas', received_contents: ['3c', '2b', '1a'] },
];
// These are the boxes for the next shipment and their contents

const boxes = [
{ code: 'gr1', contents: ['1a', '1b'] },
{ code: 'gr2', contents: ['1a', '2b'] },
{ code: 'gr3', contents: ['1b', '2c'] },
{ code: 'gr4', contents: ['2c', '3c'] },
{ code: 'gr5', contents: ['3b', '1c'] },
];

const start = new Date();
const usersWithBoxes = users.map(user => ({
  user: user,
  boxes: boxes.filter(box => box.contents.every(bc => !user.received_contents.find(rc => bc === rc)))
}));
const end = new Date();

console.log(`Function took ${end.getTime() - start.getTime()}ms to run`);
console.log(usersWithBoxes.map(u => `${u.user.name} can recieve boxes ${u.boxes.map(b => b.code)}`).join('\n'));
Adrian Brand
  • 20,384
  • 4
  • 39
  • 60