0

I have the following JS function:

let mapFunc = (key) => {
    let map = {
        a: 'first',
        b: 'first',
        c: 'first',
        d: 'second',
        e: 'second',
        f: 'second'
    }
    return map[key];
}
console.log(mapFunc('b'))

Is there a way that I can write this function so that instead of having 6 different properties, I have only 2 properties, like this?

{
    first: ['a', 'b', 'c']
    second: ['d', 'e', 'f']
}
isherwood
  • 58,414
  • 16
  • 114
  • 157
gkeenley
  • 6,088
  • 8
  • 54
  • 129
  • 4
    what result do you expect for the second data structure? – Nina Scholz Apr 06 '23 at 18:11
  • 1
    Well, you can look up the value in the array but it's not going to be more since you need to traverse `m` properties with `n` values each, so it's `O(m*n)` lookup each time. – VLAZ Apr 06 '23 at 18:11
  • Start here: https://ericlippert.com/2012/12/17/performance-rant/ – jabaa Apr 06 '23 at 18:15
  • 1
    `return Object.entries(map).find(([k, v]) => v.includes(key))?.[0]` – Barmar Apr 06 '23 at 18:23
  • 2
    @jabaa I think he means more efficient to write, since he doesn't have to write `first` and `second` over and over. – Barmar Apr 06 '23 at 18:25
  • 2
    The question is too broad. Without context we're guessing at your goals. Please revise to be more specific. – isherwood Apr 06 '23 at 18:55
  • Your first function takes a unique key and returns its corresponding string value, that much is clear. But what exactly do you expect to pass to the function you're trying to create, and what do you expect it to return? – Domino Apr 06 '23 at 19:04
  • Obviously there is a way you can write the function such that it works with the structure given. IOW, the answer to this question is "Yes". Boring. Please [edit] your question to include a real question. – Heretic Monkey Apr 06 '23 at 19:06
  • @gkeenley I'm not giving this as an answer, because you're explicitly asking about s.th. with only two properties. But if your question was supposed to be only about avoiding redundancies in code and having it readable, you might also consider a simple switch (makes more sense when the values become more complicated): `function mapFunc(key) { switch(key) { case 'a': case 'b': case 'c': return 'first'; case 'd': case 'e': case 'f': return 'second'; } return undefined; }` – Sebastian Apr 08 '23 at 08:01

3 Answers3

0

Maybe use different approach with regex and ternary operator?

const mapFunc = (k) => /\b(a|b|c)\b/.test(k) ? 'first' : /\b(d|e|f)\b/.test(k) ? 'second' : null;

console.log(mapFunc('a')); 
console.log(mapFunc('d'));

console.log(mapFunc('j'));
console.log(mapFunc('abc'));
protob
  • 3,317
  • 1
  • 8
  • 19
  • I don't want to be mean, but this is like drawing a face and erasing the features because you don't know how to draw a circle. – Domino Apr 06 '23 at 18:53
  • 1
    @Domino one can understand the OPs question as providing a simple example for a very generic question. So I think one should not only consider the given example (for which the suggested solution is certainly an overkill). There are certainly use cases where the above approach (or a modification with more cases using a switch) will be justified – Sebastian Apr 08 '23 at 07:32
  • 1
    @Sebastian I was specifically referring to the creation of two regular expressions instead of using Boolean operators. – Domino Apr 08 '23 at 20:49
  • @Domino yes, I get your point. And maybe the more general cases I had in mind when reading this answer are not really covered by the question ... – Sebastian Apr 09 '23 at 10:02
0

mapFunc will always be faster using your current 6 properties map.

Using your second data structure, you are forced to iterate over each entry to find if it contains the given value:

const mapFunc = (key) => {
  const map = {
first: ['a', 'b', 'c'],
second: ['d', 'e', 'f']
  };
  return Object.keys(map).find(aKey => map[aKey].includes(key));
};
console.log(mapFunc('b'));

You can optimize this a little by using a Set instead of an array for each properties but it will still be slower than your original code:

// `map` is created outside of `mapFunc` to initialize it only once.
const map = {
  first: new Set(['a', 'b', 'c']),
  second: new Set(['d', 'e', 'f'])
};

const mapFunc = (key) => {
  return Object.keys(map).find(aKey => map[aKey].has(key));
};
console.log(mapFunc('b'));
Raphaël
  • 3,646
  • 27
  • 28
0

You could consider transforming your desired format into your original format and using it as you do now:

const compact_map = {
    first: ['a', 'b', 'c'],
    second: ['d', 'e', 'f']
};

const convert_map = (map) =>
  Object.fromEntries(
    Object.entries(map)
      .flatMap(
        ([key, values]) => values.map((value) => [value, key])
      )
  );

const map = convert_map(compact_map);

let mapFunc = (key) => {
    return map[key];
};

console.log(mapFunc('b'));
console.log(mapFunc('d'));
console.log(mapFunc('j'));

Just a note: this is probably only a good idea in a situation where you could arrange things such that you only need to do the map conversion once. If you can do that then you have an efficient data structure in terms of the developer populating / updating it and a computationally efficient method in terms of the mapFunc function.

Ben Stephens
  • 3,303
  • 1
  • 4
  • 8