0

I have a logic that accepts a configuration and returns a value. However, I'd like to retrieve from the cache for the further tries of same configuration. The order of keys may be varied within the config but must be treated as the same configuration.

Comments explain what I try to achieve. I expect only two openings as there are only two different config in the sample code. Only first and second attempts of open() goes as expected because of the same object passing to the map as a key.

If I try to keep the keys as a JSON string then the order of the keys can be problematic.

Here is what I have tried so far. I appreciate any idea or alternative solution.

var m = new Map();

function opening(config) {
  // ...
  return "foo";
}

function open(config = {}) {

  if (!m.has(config)) {
    m.set(config, opening(config));
    console.log("open and set: ", config);
  } else {
    console.log("get from the map: ", config);
  }

  return m.get(config);
}

var c1 = { a: 1, b: 2 };

open(c1); // open and set [OK]
open(c1); // get from the map [OK] 
open({ a: 1, b: 2 }); // get from the map
open({ b: 2, a: 1 }); // get from the map, even in different orders
open({ a: 1, b: 2, c: 3 }); // open and set
open({ a: 1, c: 3, b: 2 }); // get from the map
Vahid Hallaji
  • 7,159
  • 5
  • 42
  • 51
  • That's because those two objects have different references. I've seen this same exact question asked 3 different times in the last two days... – Andrew Li Jul 13 '18 at 06:01
  • @Li357 I appreciate your linked answer. However, I don't wish to compare the equality of those objects. I believe that's a different question. – Vahid Hallaji Jul 13 '18 at 06:50
  • The answer is the same though right? You want to get a config from the cache if it has all the same entries (as the link question has an answer to) – Andrew Li Jul 13 '18 at 06:52

1 Answers1

1

It looks like you need a way to represent the different objects such that identical key-value pairs (in different orders) can be recognized as the same key in the map. One option would be to take the object and sort its entries according to the keys' alphabetical order, and then stringify the sorted entries:

var m = new Map();

function opening(config) {
  // ...
  return "foo";
}

function open(config = {}) {
  const key = objToKeyValStr(config);
  if (!m.has(key)) {
    m.set(key, opening(config));
    console.log("open and set: ", config);
  } else {
    console.log("get from the map: ", config);
  }

  return m.get(config);
}
function replacer(_, value) {
  if (typeof value !== 'object' || Array.isArray(value)) return value;
  return objToKeyValStr(value);
}
const objToKeyValStr = obj => (
  JSON.stringify(
    Object.entries(obj).sort((a, b) => a[0].localeCompare(b[0])),
    replacer
  )
);

var c1 = { a: 1, b: 2 };

open(c1); // open and set [OK]
open(c1); // get from the map [OK] 
open({ a: 1, b: 2 }); // get from the map
open({ b: 2, a: 1 }); // get from the map, even in different orders
open({ a: 1, b: 2, c: 3 }); // open and set
open({ a: 1, c: 3, b: 2 }); // get from the map

open({ a: 1, b: { c: 1, d: 1 }}); // open and set
open({ a: 1, b: { d: 1, c: 1 }}); // get from the map
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Thanks for that. In case of having a nested object, I guess I need to do the same thing recursively? Isn't it? `{ a: 1, b: 2, c: {d: 4, e: 5} }` and `{ a: 1, b: 2, c: {e: 5, d: 4} }` – Vahid Hallaji Jul 13 '18 at 06:23
  • Yes, with a custom `replacer` function passed to `JSON.stringify`, see edit - the actual key doesn't look nice, but I think it'll work, at least – CertainPerformance Jul 13 '18 at 06:59