Felipe Ossandon
Felipe Ossandon

Reputation: 43

Comparison between objects values inside a single array

I was wondering how can I do something similar to compare between plan prices or between characteristics of something but maintaining the length of items of the value with most checks described, example:


   -       |  Regulartz  |   Regulartz
   King    |     King    |   King
Individual | Individual  | Individual
   Normal  |  Normal     | Normal
   Queen   |     -       |     -
    -      |      -      | Cal King

I wanna do this comparison table, and If I do not have any value, put an "X" on it.

My arrays are silimar to this:

arr1 = [
0: {name: "King"}
1: {name: "Individual"}
2: {name: "Normal"}
3: {name: "Queen"}
]

arr2 = [
0: {name: "Regulartz"}
1: {name: "King"}
2: {name: "Individual"}
3: {name: "Normal"}
4: {name: "Cal King"}
]

...etc

I did something like

const sizeLength = sizes.map((size, idx) => (
    size.name.includes(sizes[idx].name)
  ))

But I don't have the comparison between one array with other wish contains a length > than the other.

Is any 'lodash' utility for this?

Thanks for your help!

Upvotes: 2

Views: 113

Answers (2)

adiga
adiga

Reputation: 35222

You need to

  • Create a row for each feature
  • Create a td for each plan
  • Conditionally display a checkmark based on whether the current plan has that corresponding feature

Your initial data looks something similar to this. Just creating an array of objects so that more plans can be added.

  const plans = [
    {
      planName: 'plan A',
      features: [
        { name: 'Regulartz' },
        { name: 'King' },
        { name: 'Individual' },
        { name: 'Normal' },
        { name: 'Cal King' }
      ]
    },
    {
      planName: 'plan B',
      features: [
        { name: 'King' },
        { name: 'Individual' },
        { name: 'Normal' },
        { name: 'Queen' }
      ]
    }
  ]

In render method, you could use array methods like some to check each plan and its nested features. But, it becomes very expensive operation because you'd have to do it for every td cell. So, you could create an object lookup which has each plan as key and the each feature as that plan's nested key. Also, create a unique Set of all the features in all plans.

const mapper = { },
      allFeaturesSet = new Set();

for (const { planName, features } of plans) {
  mapper[planName] = {};
  for (const { name } of features) {
    mapper[planName][name] = true;
    allFeaturesSet.add(name)
  }
}

const allFeatures = Array.from(allFeaturesSet) // convert set to array

If you have a relational database, you could get this type of data from the server itself. You could do a join between the features and the plans table to get each plan and it's feature mapping. But, since you have arrays, you'd have to loop and create a mapper.

You could also get the unique plans in a separate call like this:

  const allFeatures = Array.from(
    new Set(plans.flatMap(a => a.features.map(a => a.name)))
  );

In render, loop through the plans and create headers for each plan. Loop through each feature and create a row. Inside tr. loop through the plan and check if the mapper object has an entry for the current plan and feature and conditionally display the data

<table>
  <thead>
    <tr>
      {plans.map(a => <th>{a.planName}</th>)}
    </tr>
  </thead>
  {allFeatures.map(f => (
    <tr>
      {plans.map(a => <td>{mapper[a.planName][f] ? f : '--'}</td>)}
    </tr>
  ))}
</table>

Here's a stackblitz demo

Snippet:

function App() {
  const plans = [
    {
      planName: 'planA',
      features: [
        { name: 'Regulartz' },
        { name: 'King' },
        { name: 'Individual' },
        { name: 'Normal' },
        { name: 'Cal King' }
      ]
    },
    {
      planName: 'planB',
      features: [
        { name: 'King' },
        { name: 'Individual' },
        { name: 'Normal' },
        { name: 'Queen' }
      ]
    }
  ];
  const allFeatures = Array.from(
    new Set(plans.flatMap(a => a.features.map(a => a.name)))
  );

  const mapper = {};

  for (const { planName, features } of plans) {
    mapper[planName] = {};
    for (const { name } of features) 
      mapper[planName][name] = true;
  }

  return (
    <table>
      <thead>
        <tr>
          {plans.map(a => 
            <th>{a.planName}</th>
          )}
        </tr>
      </thead>
      {allFeatures.map(f => (
        <tr>
          {plans.map(a => (
            <td>{mapper[a.planName][f] ? f : '--'}</td>
          ))}
        </tr>
      ))}
    </table>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById("react")
);
table {
  border-collapse: collapse;
}
 th, tr, td {
  border: 1px solid black;
  padding: 5px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="react"></div>

Upvotes: 2

ISAE
ISAE

Reputation: 538

You'll have to iterate the longest array and for each element in there check each of the shorter arrays if the value exists, if it does- you're good to go, if not- add an x to it. So something similar to:

arrPremium.forEach((el, idx) => { // assuming this array holds the complete list
 if(!arrMedium.some(e => e.name == el.name)) arrMedium.splice(idx, 0, {name: "x"})
});// assuming arrMedium is the shorter array;

A better way still will be to create a new array and push the value (either name or x) to it, instead of modifying the original array, so:

let newArr = [];
arrPremium.forEach((el, idx) => { 
  let newEl = arrMedium.some(e => e.name == el.name) ? {name: el.name} : {name: "x"};
  newArr.push(newEl);
});

Upvotes: 2

Related Questions