5

I have a long list of items in an array. These need to be rendered in a component, which should be all fine and dandy. However, I need these to be wrapped by another component at every 3 items.

So, if I were to just render the items it would look like this:

return (
  <div>
    items.map((x, index) => {
      <span key={index}>{x}</span>
    })
  </div>
)

But basically, I want every three items to be wrapped inside a div with a special class, so something like this:

return (
  <div>
    <div className='group-of-3'>
      <span>Item 1</span>
      <span>Item 2</span>
      <span>Item 3</span>
    </div>
    <div className='group-of-3'>
      <span>Item 4</span>
      <span>Item 5</span>
      <span>Item 6</span>
    </div>
    .
    .
    .
  </div>
)

What would be the ideal way to do this? Keep in mind that the amount of items does change, so doing it manually is out of the question.

theJuls
  • 6,788
  • 14
  • 73
  • 160

4 Answers4

7

Using plain JavaScript, you can use Array.reduce() to create subarrays from your items. Then map on your items twice:

const group = (items, n) => items.reduce((acc, x, i) => {
  const idx = Math.floor(i / n);
  acc[idx] = [...(acc[idx] || []), x];
  return acc;
}, []);

function Example({ items }) {
  return (
    <div>{group(items, 3).map(children =>
        <div className='group-of-3'>
          {children.map((x, i) => <span key={i}>{x}</span>)}
        </div>
    )}</div>
  );
}

ReactDOM.render(
  <Example items={[1, 2, 3, 4, 5, 6, 7, 8]} />,
  document.getElementById('root')
);
.group-of-3 {
  border: 1px solid black;
  padding: 5px;
  margin: 5px;
  width: 50px;
  text-align: center;
}
<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="root"></div>
jo_va
  • 13,504
  • 3
  • 23
  • 47
3

You can use Lodash chunk to split up your array into arrays of length 3 and render those separately.

Example

function App(props) {
  const chunkedItems = _.chunk(props.items, 3);

  return (
    <div>
      {chunkedItems.map((arr, index) => (
        <div key={index}>
          {arr.map((item, index) => (
            <span key={index}>{item}</span>
          ))}
        </div>
      ))}
    </div>
  );
}

ReactDOM.render(
  <App items={[1, 2, 3, 4, 5, 6, 7]} />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<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="root"></div>
Tholle
  • 108,070
  • 19
  • 198
  • 189
1

You can use Array.from() to split the list to categories, with an internal Array.map() to render the items:

const items = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7', 'item8', 'item9', 'item10', 'item11'];

const Demo = ({ items, size = 3 }) => (
  <div>
    {Array.from(
      { length: Math.ceil(items.length / size) },
      (_, i) => (
        <div key={i} className='group-of-3'>
        {items.slice(i * size, (i + 1) * size)
          .map((item) => <span key={item}>{item}</span>)
        }
        </div>
      )
    )}
  </div>
);


ReactDOM.render(
  <Demo items={items} />,
  demo
);
.group-of-3 {
  margin: 1em 0;
  border: 1px solid black;
}

span {
  display: block;
}
<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="demo"></div>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

You could start by grouping the elements of the array into multiple smaller arrays, each with 3 elements.

const arr = [1,2,3,4,5,6,7,8,9];

const grouped = arr.reduce((res, el, i) => {
  if (i % 3 === 0) {
    res[res.length] = [el];
  } else {
    res[res.length-1] = [...res[res.length-1], el];
  }
  return res;
}, []);

console.log(grouped);
Olian04
  • 6,480
  • 2
  • 27
  • 54