6
\$\begingroup\$

I would like to get a review of something,

I have a function that creates an array of objects for a Drop-Down component (react-select).

The function gets data, valueKey, labelKey and otherInfo from an object, and creates the array of objects based on the valueKey and the labelKey.

const createOptions = ({data, valueKey, labelKey, ...otherInfo}) => {
  return data.map(item => 
    ({ 
      value: item[valueKey],
      label: item[labelKey],
      ...appendOtherInfo(item, otherInfo)
    })
  );
}

most of the time, valueKey and labelKey are enough, but there are some cases where there is additional info to add to each object, which is otherInfo, which is an object as well.

appendOtherInfo() function creates an object which is then appended to each object that map iteration returned.

const appendOtherInfo = (item, otherInfo) => {
  return Object.entries(otherInfo).reduce((total, [key, value]) => {
    total[key] = item[value];

    return total;
  }, {});
}

for example, after calling createOptions() with the array:

const people = [
  {id: '123', name: 'Dani', type: 'a', city: 'Rome'},
  {id: '222', name: 'John', type: 'a', city: 'London'},
  {id: '333', name: 'David', type: 'b', city: 'Madrid'}
]

and the rest of the info to create the new array,

createOptions({
  data: people,
  valueKey: 'id',
  labelKey: 'name',
  type: 'type',
  capitalCity: 'city'
});

The result is:

[
  {
    "value": "123",
    "label": "Dani",
    "type": "a",
    "capitalCity": "Rome"
  },
  {
    "value": "222",
    "label": "John",
    "type": "a",
    "capitalCity": "London"
  },
  {
    "value": "333",
    "label": "David",
    "type": "b",
    "capitalCity": "Madrid"
  }
]

In the appendOtherInfo() function, I used Object.entries and reduce to create the additional info for each object. It's working as expected, but I'd like to know if there is a better/easier way to achieve the same result.

\$\endgroup\$
2
\$\begingroup\$

Your solution is okay; although I'd make appendOtherInfo() a local function of createOptions(). But the problem is, in terms of abstraction - you build a function that you want to re-use and that gives you the feeling it is generic enough for all future use cases for your drop down. However, there are two problems - your "generic" function is just generic enough for flat objects and cannot easily handle nested objects.

Furthermore because it's only argument is an object, different properties in it have different meanings - this is not good for code readability.

So my advice is, why bother writing something that is seemingly generic, but actually not generic enough that it will ever see proper reuse.

Instead simply use direct mappers; they are more readable and they give you the full flexibility, so instead of:

<react-select options = { createOptions({
  data: people,
  valueKey: 'id',
  labelKey: 'name',
  type: 'type',
  capitalCity: 'city'
})}>/

Simply do:

<react-select options = { people.map(peopl => {
  value: peopl.id,
  label: peopl.name,
  type: peopl.type
  capitalCity: peopl.city
}
}>/

(I did not look up <react-select>'s API): Do not hide simple extracting and remapping behind functions if there is no need to - make code explicit and readable.

You can happily use destructuring with renaming and ..., if you only need to map label and value as well:

<react-select options = { people.map(peopl => ({
  const {id: value, name: label, ...other} = peopl;
  return {value, name, ...other}})
)}>/

Or to be explicit and combining above:

<react-select options = { people.map(peopl => ({
  const {id: value, name: label, type, city: capitalCity} = peopl;
  return {value, name, type, capitalCity}})
)}>/
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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