2

I have the array:

array = [
  {
    "id": 1,
    "date": {
      "id": 1,
      "name": "202001"
    },
    "item": {
      "id": 1,
      "name": "I1"
    },
    "price": 100
    },
    {
    "id": 2,
    "date": {
      "id": 2,
      "name": "202002"
    },
    "item": {
      "id": 1,
      "name": "I1"
    },
    "price": 200
  },
  {
    "id": 3,
    "date": {
      "id": 2,
      "name": "202002"
    },
    "item": {
      "id": 2,
      "name": "I2"
    },
    "price": 300
  },
]

And I want to be able to display the data as shown in the table:

ITEM 202001 202002
I1 100 200
I2 - 300

The idea is to group the values for each date for the same item.

How can I do it, suggestions?

bonnegnu
  • 175
  • 3
  • 12

1 Answers1

1

You could reduce the whole structure into a dictionary indexed by item name whose values are dictionaries indexed by dates:

items_dicc = array.reduce((acc, e) => {
  if (!acc[e["item"]["name"]]) {
    acc[e["item"]["name"]] = {
      [e["date"]["name"]]: e["price"]
    }
  } else {
    acc[e["item"]["name"]][e["date"]["name"]] = e["price"]
  }
  return acc
}, {})

/* items_dicc holds:
{ I1: { '202001': 100, '202002': 200 }, I2: { '202002': 300 } }
*/

Subsequently, you could take advantage of the items_dicc structure to build the table you're looking for:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <table>
    <thead>
      <tr class="thead">
        <th>ITEM</th>
      </tr>
    </thead>
    <tbody class="tbody">
      <tr>
        <td></td>
      </tr>
    </tbody>
  </table>

</body>

<script>
  array = [
    {
      "id": 1,
      "date": {
        "id": 1,
        "name": "202001"
      },
      "item": {
        "id": 1,
        "name": "I1"
      },
      "price": 100
    },
    {
      "id": 2,
      "date": {
        "id": 2,
        "name": "202002"
      },
      "item": {
        "id": 1,
        "name": "I1"
      },
      "price": 200
    },
    {
      "id": 3,
      "date": {
        "id": 2,
        "name": "202002"
      },
      "item": {
        "id": 2,
        "name": "I2"
      },
      "price": 300
    }
  ]

  items_dicc = array.reduce((acc, e) => {
    if (!acc[e["item"]["name"]]) {
      acc[e["item"]["name"]] = {
        [e["date"]["name"]]: e["price"]
      }
    } else {
      acc[e["item"]["name"]][e["date"]["name"]] = e["price"]
    }
    return acc
  }, {})

  // Compute dates that will be used as headers. Duplicates are removed after creating a set and turning it into a list again
  dates = [...new Set(Object.keys(items_dicc).map(i => Object.keys(items_dicc[i])).flat())]


  const thead = document.getElementsByClassName("thead")[0]
  const tbody = document.getElementsByClassName("tbody")[0]

  dates.forEach(date => {
    thead.appendChild(
      htmlToElement(`<th>${date}</th>`)
    )
  });

  Object.keys(items_dicc).forEach(i => {
    let row = `<tr><td>${i}</td>`
    dates.forEach(date => {
      row = `${row}<td>${items_dicc[i][date] || ''}</td>`
    });
    row = `${row}</tr>`
    tbody.appendChild(htmlToElement(row))
  })

  // From https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
  function htmlToElement(html) {
    var template = document.createElement('template');
    html = html.trim();
    template.innerHTML = html;
    return template.content.firstChild;
  }

</script>

</html>

The final result:

<table>
  <thead>
    <tr class="thead">
      <th>ITEM</th>
      <th>202001</th>
      <th>202002</th>
    </tr>
  </thead>
  <tbody class="tbody">
    <tr>
      <td></td>
    </tr>
    <tr>
      <td>I1</td>
      <td>100</td>
      <td>200</td>
    </tr>
    <tr>
      <td>I2</td>
      <td></td>
      <td>300</td>
    </tr>
  </tbody>
</table>

EDIT: Simplest React version of the solution:

App.js

function App() {
  const array = [
    {
      "id": 1,
      "date": {
        "id": 1,
        "name": "202001"
      },
      "item": {
        "id": 1,
        "name": "I1"
      },
      "price": 100
    },
    {
      "id": 2,
      "date": {
        "id": 2,
        "name": "202002"
      },
      "item": {
        "id": 1,
        "name": "I1"
      },
      "price": 200
    },
    {
      "id": 3,
      "date": {
        "id": 2,
        "name": "202002"
      },
      "item": {
        "id": 2,
        "name": "I2"
      },
      "price": 300
    }
  ]

  const items_dicc = array.reduce((acc, e) => {
    if (!acc[e["item"]["name"]]) {
      acc[e["item"]["name"]] = {
        [e["date"]["name"]]: e["price"]
      }
    } else {
      acc[e["item"]["name"]][e["date"]["name"]] = e["price"]
    }
    return acc
  }, {})

  // Compute dates that will be used as headers. Duplicates are removed after creating a set and turning it into a list again
  const dates = [...new Set(Object.keys(items_dicc).map(i => Object.keys(items_dicc[i])).flat())]

  return (
    <div className="App">
      <table>
        <thead>
          <tr>
            <th>ITEM</th>
            {dates.map(date => <th>{date}</th>)}
          </tr>
        </thead>
        <tbody>
          {
            Object.keys(items_dicc).map((item) => {
              return (
                <tr>
                  <td>{item}</td>
                  {dates.map((date) => <td>{items_dicc[item][date] || ''}</td>)}
                </tr>
              )
            })
          }
        </tbody>
      </table>
    </div>
  );
}

export default App;
Turtlean
  • 579
  • 4
  • 9
  • Thanks a lot! I appreciate what you did !! It looks great! How to do the same in react.js? – bonnegnu Dec 19 '20 at 20:08
  • You're welcome =). I edited the answer so you can see a `React` version of the solution – Turtlean Dec 19 '20 at 23:24
  • If I wanted to add the functionality to show more details of an item in a popup, what would it be like? – bonnegnu Dec 20 '20 at 11:17
  • Hmmm, I would suggest posting a new question for that as it's quite unrelated to the original one – Turtlean Dec 20 '20 at 13:33
  • Ok I'll do it. Thank you! – bonnegnu Dec 20 '20 at 18:36
  • Hello @Turtlean I have posted one more detail that I would need to understand to keep learning and developing: https://stackoverflow.com/questions/65390482/show-a-pop-up-window-with-information-coming-from-an-element-and-its-totals-fro You can help me with that? Thank you! – bonnegnu Dec 21 '20 at 09:49