2

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!

5
  • What do the 2 arrays have to do with the table above? What is sizes array? Commented May 6, 2021 at 16:18
  • I want to put an "----" if the comparison it doesn't match Commented May 6, 2021 at 16:37
  • Which one of the arrays does determines the position of elements? is it just the longes one?, also the table is showing 3 arrays while in code you only show 2, which makes it harder to understand what you want Commented May 6, 2021 at 16:41
  • I'm guessing what he wants is to compare 2 arrays and do something similar to a left join where a non-matching element of array1 to array2 is market as '---', but thats just my opinion. Would this solve the problem arr1.map(val => arr2.some(x => x.name == val.name) ? val.name : '---') ? But we still don't know which array we're comparing the others to or maybe we're comparing all arrays against each other, in which case this wouldn't make sense :( Commented May 6, 2021 at 16:46
  • @Bargros If your guess is right than your suggested arr1.map won't work, it'll return ["king", "individual", normal", "---"] Commented May 6, 2021 at 16:53

2 Answers 2

2

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>

Sign up to request clarification or add additional context in comments.

2 Comments

You rocks bro ,ty!
How can I do this without the 'planName' ? @adiga
2

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);
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.