1
\$\begingroup\$

I am trying to exclude some elements from array items that include properties from array excludes. I wrote this code which works fine. However, due to nested iterations, time complexity is O(n^3). Is there a way to do better?

const items = [
  { color: 'red', type: 'tv', age: 18 },
  { color: 'silver', type: 'phone', age: 20 },
  { color: 'blue', type: 'car', age: 18 },
  { color: 'green', type: 'tv', age: 10 },
  { color: 'gold', type: 'phone', age: 7 },
  { color: 'orange', type: 'car', age: 2 },
];

const excludes = [
  { k: 'color', v: 'red' },
  { k: 'color', v: 'blue' },
  { k: 'type', v: 'phone' },
];

function excludeItems(items, excludes) {
  let ref = new Map();
  excludes.forEach((pair) => {
    if (!ref.has(pair.k)) {
      ref.set(pair.k, []);
    }
    ref.get(pair.k).push(pair.v);
  });

  ref.forEach((value, key) => {
    items = items.filter(
      (item) => !value.some((element) => element == item[key]),
    );
  });

  return items;
}

This is the console output (which is correct):

Remaining Items: [
  { color: 'green', type: 'tv', age: 10 },
  { color: 'orange', type: 'car', age: 2 }
]
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Although you are using a map for the keys, you're still iterating over all the values in the excludes list -- so the map isn't doing as much work for you as it could be. If, instead, you use composite keys so the key and value are combined into a key in the map, then you can use the efficient map lookup instead of iterating over excludes.

You do still have to iterate over the properties of item, but I assume the number of properties on an item is a constant and is several orders of magnitude smaller than the size of items and excludes? Given m excludes and n items, with p properties on an item, you get O(m+pn), but we've said p is constant so that's O(m+n), provided you're getting O(1) performance from your Map lookup.

const items = [
  { color: 'red', type: 'tv', age: 18 },
  { color: 'silver', type: 'phone', age: 20 },
  { color: 'blue', type: 'car', age: 18 },
  { color: 'green', type: 'tv', age: 10 },
  { color: 'gold', type: 'phone', age: 7 },
  { color: 'orange', type: 'car', age: 2 },
];

const excludes = [
  { k: 'color', v: 'red' },
  { k: 'color', v: 'blue' },
  { k: 'type', v: 'phone' },
];

function excludeItems(items, excludes) {
  let excludesMap = new Map();
  excludes.forEach((pair) => {
    const compositeKey = pair.k + '|' + pair.v;
    excludesMap.set(compositeKey, true);
  });

  return items.filter(item => {
    const doExclude = Object.getOwnPropertyNames(item).some(x => excludesMap.has(x + '|' + item[x]));
    return !doExclude;
  });
}

console.log(excludeItems(items, excludes));

By the way, I chose | as the delimiter between key and value because I assumed that you don't have any properties with | in the key name. If that's not true, you would have to pick a different delimiter.

\$\endgroup\$
6
  • \$\begingroup\$ This would make it O(n^2), correct? \$\endgroup\$
    – noobie
    Commented Jan 6, 2022 at 1:39
  • \$\begingroup\$ I could be wrong, but I'm not seeing what would make it n^2. It's not all excludes times all items. If there are 500 excludes and 10,000 items, then this code will perform roughly 10500 operations, not 5 million, right? Or am I missing something? \$\endgroup\$ Commented Jan 6, 2022 at 1:47
  • \$\begingroup\$ Likewise, I think your code is n^2 not n^3. Unless the number of properties in the object is unbounded. Your code says "for each excluded property, for each item, for each excluded value". If the distinct excluded property count is constant, that's each item x each excluded value. \$\endgroup\$ Commented Jan 6, 2022 at 1:50
  • \$\begingroup\$ It's the nested loops that makes it O(n^2). First there is .filter() which iterates over items, and then for each item, you iterate over its properties using .some(). \$\endgroup\$
    – noobie
    Commented Jan 6, 2022 at 1:51
  • \$\begingroup\$ Yes. I said "but I assume the number of properties on an item is a constant and is several orders of magnitude smaller than the size of items and excludes" -- but it sounds like that assumption is wrong -- you're saying that the number of properties is significant to the performance. In which case the improvement I've suggested gets you from O(n^3) to O(n^2) like you said. However, I haven't seen many applications where you have as many properties in an object as you have objects in a list, so I'm skeptical that the number of properties isn't effectively constant. \$\endgroup\$ Commented Jan 6, 2022 at 1:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.