0
\$\begingroup\$

I have the object with values. I trying to filter based on values.

var data = {
  "map": {
    "0": ["abcd"],
    "1": ["efgh"],
    "2": ["xyz"],
  }
}

In above object I am trying to filter xyz which are have in array values.

Expecting output:

 { '0': [ 'xyz' ] }

I done the code but I am not sure it's efficient way. Can someone help me this.

const  data = {
  "map": {
    "0": ["abcd"],
    "1": ["efgh"],
    "2": ["xyz"],
    "3": ["abcd", "xyz"],
  }
}

const filtered = Object.keys(data.map)
  .filter((key) => {
    return data.map[key].includes('xyz');
  })
  .reduce((obj, key, cIndex) => {
    obj[cIndex] = data.map[key];
    return obj;
  }, {});
  
  console.log('filtered', filtered);

\$\endgroup\$
2
  • \$\begingroup\$ Should I assume the input will always be a dictionary whose keys are integers from 0 to n, and the order should also be kept in output? \$\endgroup\$
    – tsh
    Jan 22, 2021 at 6:27
  • \$\begingroup\$ yes. the output order always should start from 0 \$\endgroup\$
    – RSKMR
    Jan 22, 2021 at 7:20

2 Answers 2

1
\$\begingroup\$

Avoid use dictionary when you need Array

I'm not sure why you would prefer to store your Array-like data in a dictionary. I would suggest use a real Array if you need an Array.

Order of Object.keys

You may read more details about the order of Object.keys return values from this post: Does ES6 introduce a well-defined order of enumeration for object properties? The order of Object.keys is defined in ES2020. It may not work as you want in older environments.

Anyway, relay on the order of Object.keys don't seems to be a good idea to me. You may always sort the keys after Object.keys if you want though.

Use Object.assign to convert to / from Array

When you need an Array, use an Array. You can try Object.assign which convert your dictionary into Array and vice versa. (But this only applied to "when you cannot change the interface due to any reason").

function filterData(data, value) {
    const arrayData = Object.assign([], data.map);
    const filteredArrayData = arrayData.filter(list => list.includes(value));
    const result = Object.assign({}, filteredArrayData);
    return result;
}
\$\endgroup\$
4
  • \$\begingroup\$ Thanks for your response. When I add this in typescript I am getting error in includes.. Error details (Property 'includes' does not exist on type 'never'.ts(2339)) \$\endgroup\$
    – RSKMR
    Jan 22, 2021 at 7:44
  • \$\begingroup\$ @RSKMR Maybe you want to change your question to TypeScript? \$\endgroup\$
    – tsh
    Jan 22, 2021 at 7:53
  • \$\begingroup\$ Thanks @tsh for contributing this great answer - I hope to see more from you in future! \$\endgroup\$ Jan 22, 2021 at 7:57
  • \$\begingroup\$ @RSKMR TypeScript seems not smart enough to know types correctly. So some internal variables may help. \$\endgroup\$
    – tsh
    Jan 22, 2021 at 8:13
0
\$\begingroup\$

Review

  • the whole filter chain could be on basis of value instead of key from data since there's no real use of key here

filter part

  • technically your approach is perfect, just that it could be concise/readable
  • you can simplify the filter as in below snippet, simply because readability is one of the TOP priority of a good code IMHO

map part

  • instead of juggling the same object inside reduce between all elements we can simply get the required objects out of it.

  • one other major thing is that reduce as the name suggests is for when you wish to reduce N elements into exactly 1 element by some logic, which clearly isn't the case here & thus i think map should be preferred.

  • we can use map here since at the end we want an array of objects, so here we can return object for each key which at end gets collected as array

  • apart from these the redability point again applies here as well, since this can also be simplified very much, refer the snippet to get an idea

  • quoting a point from Mozilla's doc on reduce

The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.

Alternate approach

const  data = {
  "map": {
    "0": ["abcd"],
    "1": ["efgh"],
    "2": ["xyz"],
    "3": ["abcd", "xyz"],
  }
}

const filtered = Object.values(data.map)
  .filter(value => value.includes('xyz'))
   // the () after the "=>" are used to wrap returned result into object
   // the [i] inside ({..}) is used for setting the key dynamically
  .map((value, i) => ({[i]: value}));
  
console.log('filtered', filtered);

  • The data structure seems to be object representation of array, so if possible prefer 2D array over object with 0 based keys, this would make things more simple

PS: answer is updated after useful suggestions of @Toby Speight & @Graipher in comments

\$\endgroup\$
3
  • 1
    \$\begingroup\$ You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer. \$\endgroup\$ Jan 22, 2021 at 7:56
  • 2
    \$\begingroup\$ @TobySpeight Well, there is some review here, it is just hidden in the comments in the code. This would be a lot better answer if you pulled it out of the comments and put it into text, Harsh Gundecha. \$\endgroup\$
    – Graipher
    Jan 22, 2021 at 11:00
  • \$\begingroup\$ I easily miss comments in the code; I too prefer descriptive text outside of the code block. That's easier to tell apart from a review focusing on the commenting of the code, for one thing. \$\endgroup\$ Jan 22, 2021 at 11:07

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.