2

I have this object:

const cOPE= { ADD:   43
            , SUB: 8722
            , MUL:  261
            , DIV:  247
            , EQU:   61
            , Point: 46
            , Clear: 99 
            }

and I currently use:

const kOPE = k => Object.entries(cOPE).find(o=>o[1]==k)[0]

to retrieve a key value based on a value. example:

 console.log( kOPE(261) ) // -> MUL

Is there a better way to code the kOPE function?
Isn't there simply a JS function that can return the name of a key?

[+] this code is internal in a closed object, other values cannot exist and each key is unique.

Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
  • 5
    "Better" by which metric? – Felix Kling Jan 22 '20 at 22:18
  • Potential issue with your current code is that an error will be thrown is nothing is found – CertainPerformance Jan 22 '20 at 22:19
  • @FelixKling I find my method a bit far-fetched, I wonder if there is not a JS method that does this directly ... – Mister Jojo Jan 22 '20 at 22:21
  • Get the list of key value pairs, find the one with the matching value, get the key. What's far-fetched about that? – Taplar Jan 22 '20 at 22:22
  • @CertainPerformance this code is internal in a closed object, other values cannot exist – Mister Jojo Jan 22 '20 at 22:23
  • If the object is static, you might consider constructing a reverse object - reverse the keys and the values, then just use ordinary property lookup – CertainPerformance Jan 22 '20 at 22:25
  • One small thing I might do, just because I like naming things, I might would deconstruct the find input to name them. But that's just my personal flavor. `Object.entries(cOPE).find(([key, value]) => value == k)[0];` – Taplar Jan 22 '20 at 22:27
  • @CertainPerformance I thought about it, but I prefer to avoid multiplying objects. - Isn't there simply a JS function that can return the name of a key? – Mister Jojo Jan 22 '20 at 22:31
  • ¿What should happen if two keys have the same value? that might be a painful bug in the future. – Japsz Jan 22 '20 at 22:36

3 Answers3

3

If you find yourself doing that often, you may want to consider inverting your map:

const invert =
  obj =>
    Object.fromEntries(
      Object.entries(obj)
        .map(([k, v]) => [v, k]));

const cOPE =
  { ADD:   43
  , SUB: 8722
  , MUL:  261
  , DIV:  247
  , EQU:   61
  , Point: 46
  , Clear: 99 
  };

const EPOc = invert(cOPE);

EPOc[261];
//=> MUL

Performance

If you do worry about performance, you should definitely use an inverted map.

Here's a curried version of invert that returns a function that accepts a value and returns the key associated to it. It's bound to the inverted object built from obj.

Using the same test function from Rick Hitchcock:

const invert =
  obj => ((o, v) => o[v])
    .bind(null,
      Object.fromEntries(
        Object.entries(obj)
          .map(([k, v]) => [v, k])));

const cOPE =
  { ADD:   43
  , SUB: 8722
  , MUL:  261
  , DIV:  247
  , EQU:   61
  , Point: 46
  , Clear: 99 
  };


test(invert(cOPE), 43);
test(invert(cOPE), 261);
test(invert(cOPE), 99);
<script>
// Credits to Rick Hitchcock for the test function
const test = (fnc, num) => {
  let j;
  console.time('speedTest')
  for (let i = 0; i < 1000000; i++) { j = fnc(num) }
  console.timeEnd('speedTest')
}
</script>
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
customcommander
  • 17,580
  • 5
  • 58
  • 84
2

This code is much faster than yours (in Chrome at least):

const kOPE = k => Object.keys(cOPE)[Object.values(cOPE).indexOf(k)]

Even though objects are not guaranteed to be in order, I would assume Object.keys and Object.values would always be in the same order as each other.

Comparisons:

const cOPE= { ADD:   43, SUB: 8722, MUL:  261, DIV:  247, EQU:   61, Point: 46, Clear: 99}
const kOPE1 = k => Object.entries(cOPE).find(o=>o[1]==k)[0]
const kOPE2 = k => Object.keys(cOPE)[Object.values(cOPE).indexOf(k)]

const test = (fnc, num) => {
  let j = fnc(num),
      timer = `${num} ${j}`;
  
  console.time(timer);
  for (var i = 0; i < 1000000; i++) {
    j = fnc(num)
  }
  console.timeEnd(timer);
}

console.log('Object.entries:')
test(kOPE1, 43);
test(kOPE1, 261);
test(kOPE1, 99);

console.log('_'.repeat(100))

console.log('Object.keys:')
test(kOPE2, 43);
test(kOPE2, 261);
test(kOPE2, 99);
Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
0

This way you can replace null by any initial state you want instead.

const cOPE= { ADD:   43
            , SUB: 8722
            , MUL:  261
            , DIV:  247
            , EQU:   61
            , Point: 46
            , Clear: 99 
            }


const kOPE = k => Object
                    .entries(cOPE)
                    .reduce((obj, o)=>o[1]===k?o[0]:obj,null)
                    
console.log( kOPE(261) )

const test = (fnc, num) => {
  let j;
  console.time('speedTest')
  for (let i = 0; i < 1000000; i++) { j = fnc(num) }
  console.timeEnd('speedTest')
}

test(kOPE, 43);
test(kOPE, 261);
test(kOPE, 99);
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
Japsz
  • 785
  • 6
  • 14