1

I have an array of users that I need to be filtered and ordered according to another array "filter". Reading the code bellow you can see the expected result.

const users = [ 
  { key: 'abc', name: 'Anna', age: 22, gender: 'F' }, 
  { key: 'def', name: 'John', age: 25, gender: 'M' },
  { key: 'ghi', name: 'Mary', age: 27, gender: 'F' },
  { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' } 
] 

const filter = [ 
  { key: 'jkl' },
  { key: 'def' },
  { key: 'abc' },
] 

// Here is the expected result:
const expected_result = [ 
  { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' },
  { key: 'def', name: 'John', age: 25, gender: 'M' },
  { key: 'abc', name: 'Anna', age: 22, gender: 'F' }
] 

Please consider that the "key" attribute is unique. Does anyone know how I can do this? If you can also explain the logic behind your code I would appreciate it. I'm a begginer and I'm struggling with how to use the JavaScript functions to do this.

Thanks in advance!

  • I don't understand, isn't this the same question you [posted yesterday](https://stackoverflow.com/questions/64030984/how-to-filter-an-array-of-objects-with-names-from-another-array-of-objects), just using `key` instead of `name`? Or am I missing something? – Nick Parsons Sep 24 '20 at 13:18
  • 1
    now i needed it to be also ordered, not just filtered, according to another array... i added the key to make a unique property for the object – Amanda Santos Sep 24 '20 at 13:20

4 Answers4

2

const users = [ 
  { key: 'abc', name: 'Anna', age: 22, gender: 'F' }, 
  { key: 'def', name: 'John', age: 25, gender: 'M' },
  { key: 'ghi', name: 'Mary', age: 27, gender: 'F' },
  { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' } 
] 

const filter = [ 
  { key: 'jkl' },
  { key: 'def' },
  { key: 'abc' },
] 

function sort(user, filter) {
  var results = [];
  filter.forEach(obj => {
      user.forEach(obj2 => {
          if (obj.key == obj2.key) {
            results.push(obj2);
}
          })
      })
return results;
  }
  console.log(sort(users, filter))
Alpha Wolf Gamer
  • 285
  • 1
  • 10
2

You can convert your first array of users to a Map. A Map is similar to an object, however, it has some differences (I'm using a Map here as it's easier to construct from an array compared to an object).

The Map would have the following shape:

Map {
  "abc": { key: 'abc', name: 'Anna', age: 22, gender: 'F' }, 
  "def": { key: 'def', name: 'John', age: 25, gender: 'M' },
  "ghi": { key: 'ghi', name: 'Mary', age: 27, gender: 'F' },
  "jkl": { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' } 
}

In the code below, the above Map is stored in a variable called lut (short for look-up-table). Doing lut.get("def") will return the object stored at the key "def", in this case that object is:

{ key: 'def', name: 'John', age: 25, gender: 'M' }

JavaScript engines have optimized this look-up to be incredibly efficient, so creating a Map like this can help improve the overall scalability, meaning it will be efficient if you have many users.

Since order matters in your output, you can use .map() on your filter array (which determines the order) to convert each object with the name property to the object in the Map we built. To perform the "conversion" you can return the new object you want, which you can obtain from your Map using .get(o.key).

See working example below:

const users = [{ key: 'abc', name: 'Anna', age: 22, gender: 'F' }, { key: 'def', name: 'John', age: 25, gender: 'M' }, { key: 'ghi', name: 'Mary', age: 27, gender: 'F' }, { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' }]; 
const filter = [{ key: 'jkl' }, { key: 'def' }, { key: 'abc' }]; 
const expected_result = [{ key: 'jkl', name: 'Joe',  age: 30, gender: 'M' }, { key: 'def', name: 'John', age: 25, gender: 'M' }, { key: 'abc', name: 'Anna', age: 22, gender: 'F' }];

const lut = new Map(users.map(o => [o.key, o]));
const result = filter.map(f => lut.get(f.key));
console.log(result);

If one of your objects in the filter can have a key for a user which isn't in the users list, the above approach will transform that filter object into "undefined". If you want to ignore it, you can use .filter() before you map. Using .filter() will remove all filter objects which don't have a key within the map:

const users = [{ key: 'abc', name: 'Anna', age: 22, gender: 'F' }, { key: 'def', name: 'John', age: 25, gender: 'M' }, { key: 'ghi', name: 'Mary', age: 27, gender: 'F' }, { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' }]; 
const filter = [{ key: 'nonUserKey' }, { key: 'def' }, { key: 'abc' }]; 
const expected_result = [{ key: 'jkl', name: 'Joe',  age: 30, gender: 'M' }, { key: 'def', name: 'John', age: 25, gender: 'M' }, { key: 'abc', name: 'Anna', age: 22, gender: 'F' }];

const lut = new Map(users.map(o => [o.key, o]));
const result = filter.filter(o => lut.has(o.key)).map(f => lut.get(f.key));
console.log(result);
Nick Parsons
  • 31,322
  • 6
  • 25
  • 44
1

A pretty efficient solution would be to convert the filter array to a map, and then use it to filter the users:

const users = [ 
  { key: 'abc', name: 'Anna', age: 22, gender: 'F' }, 
  { key: 'def', name: 'John', age: 25, gender: 'M' },
  { key: 'ghi', name: 'Mary', age: 27, gender: 'F' },
  { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' } 
] 

const filter = [ 
  { key: 'jkl' },
  { key: 'def' },
  { key: 'abc' },
] 

const keys = Object.values(filter).reduce((acc, cur) => { acc[cur.key] = true; return acc; }, {});
const result = users.filter(user => keys[user.key]);

console.log(result);
MorKadosh
  • 5,128
  • 3
  • 23
  • 34
  • I haven't understood your code so well, but I don't think it's ordered... I needed it to be filtered and ordered according to the filter array – Amanda Santos Sep 24 '20 at 13:17
0

You can try this approach:

Logic:

  • Create a map from filters with signature: <key>: <index>
  • Filter users array with objects that have index in filterIndex
  • Sort this filtered array using index stored in filterIndex

const users = [ { key: 'abc', name: 'Anna', age: 22, gender: 'F' }, { key: 'def', name: 'John', age: 25, gender: 'M' }, { key: 'ghi', name: 'Mary', age: 27, gender: 'F' }, { key: 'jkl', name: 'Joe',  age: 30, gender: 'M' } ] 

const filter = [ { key: 'jkl' }, { key: 'def' }, { key: 'abc' }, ];

const filterIndex = filter.reduce((acc, filterObj, index) => {
  acc[filterObj.key] = index;
  return acc;
}, {})

const result = users
  .filter(({ key }) => filterIndex[key] !== undefined)
  .sort((a, b) => {
    return filterIndex[ a.key ] - filterIndex[ b.key ]
  });

console.log(result)
Rajesh
  • 21,405
  • 5
  • 35
  • 66