I have a resulting table from a backend that returns products and quantities. What i need to achieve is to check for repeating products and sum their quantities in a Overview below the table. I’ve written javascript, that partially works, but with more data i saw that it is not returning correct values and the summary shows too big numbers.
If you delete a chunk of html with rows from the table it works as expected, but cannot find the problem at this point. Also my code may not be the optimal to achieve what i need so any suggestions are welcome.
function updateTotalQuantityNumber() { var total = 0; jQuery(".quantity-number").each(function () { total += parseInt(jQuery(this).text()); }); jQuery("#total-requests-number").text(total); } updateTotalQuantityNumber(); //create object from the #requests-table rows function getRequests() { var requests = []; jQuery("#requests-table tbody tr").each(function () { var request = {}; request.product = jQuery(this).find(".product-name").text(); request.quantity = jQuery(this).find(".quantity-number").text(); requests.push(request); }); console.log(requests); return requests; } //get the unique product names from the object array and sum their quantities function getUniqueProductNames() { var uniqueProductNames = []; var requests = getRequests(); for (var i = 0; i < requests.length; i++) { uniqueProductNames.push(requests[i].product); } //sum the quantities of the unique product names let uniqueItems = [...new Set(uniqueProductNames)]; var uniqueProductNamesQuantity = []; console.log(uniqueItems); for (var i = 0; i < uniqueItems.length; i++) { var quantity = 0; for (var j = 0; j < requests.length; j++) { if (uniqueProductNames[i] == requests[j].product) { quantity += parseInt(requests[j].quantity); } } uniqueProductNamesQuantity.push(quantity); console.log(quantity); } //create object from the unique product names and their quantities and render the results var uniqueProductNamesObject = []; for (var i = 0; i < uniqueItems.length; i++) { var uniqueProductNamesObjectItem = {}; uniqueProductNamesObjectItem.product = uniqueItems[i]; uniqueProductNamesObjectItem.quantity = uniqueProductNamesQuantity[i]; //alre uniqueProductNamesObject.push(uniqueProductNamesObjectItem); } console.log(uniqueProductNamesObject); var resultCards = jQuery("#result-total"); resultCards.empty(); for (var i = 0; i < uniqueProductNamesObject.length; i++) { let final_data = `<div class="col"> <div class="card card-body shadow"> <div class="d-inline-flex align-items-center" style="min-height:128px"> <div class="me-2"> <div class="bg-light p-3 rounded-circle"> <svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" fill="currentColor" class="bi bi-box" viewBox="0 0 16 16"> <path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5 8 5.961 14.154 3.5 8.186 1.113zM15 4.239l-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923l6.5 2.6zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464L7.443.184z"/> </svg> </div> </div> <div> <span class="fw-bold display-5 mb-5" data-vanilla-counter="" data-start-at="0" data-end-at="125" data-time="1000" data-delay="60" data-format="{}">${uniqueProductNamesObject[i].quantity}</span> <p class="lead" editable="inline">${uniqueProductNamesObject[i].product}</p> </div> </div> </div> </div>`; resultCards.append(final_data); } } getUniqueProductNames();
<html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.min.js"></script> <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> </head> <body> <table class="table" id="requests-table"> <thead> <tr> <th scope="col">Product</th> <th scope="col">Quantity</th> <th scope="col">Customer</th> </tr> </thead> <tbody> <!--fwp-loop--> <tr> <td class="product-name">Product 4</td> <td class="quantity-number">57</td> <td>Nop</td> </tr> <tr> <td class="product-name">Product 6</td> <td class="quantity-number">1</td> <td>Nop</td> </tr> <tr> <td class="product-name">Product 3</td> <td class="quantity-number">29</td> <td>Klm</td> </tr> <tr> <td class="product-name">Product 3</td> <td class="quantity-number">54</td> <td>Jkl</td> </tr> <tr> <td class="product-name">Product 4</td> <td class="quantity-number">27</td> <td>Jkl</td> </tr> <tr> <td class="product-name">Product 5</td> <td class="quantity-number">71</td> <td>Jkl</td> </tr> <tr> <td class="product-name">Product 6</td> <td class="quantity-number">28</td> <td>Jkl</td> </tr> <tr> <td class="product-name">Product 1</td> <td class="quantity-number">12</td> <td>Def</td> </tr> <tr> <td class="product-name">Product 2</td> <td class="quantity-number">40</td> <td>Def</td> </tr> <tr> <td class="product-name">Product 3</td> <td class="quantity-number">49</td> <td>Def</td> </tr> <tr> <td class="product-name">Product 4</td> <td class="quantity-number">71</td> <td>Def</td> </tr> <tr> <td class="product-name">Product 5</td> <td class="quantity-number">29</td> <td>Def</td> </tr> <tr> <td class="product-name">Product 6</td> <td class="quantity-number">63</td> <td>Def</td> </tr> <tr> <td class="product-name">Product 1</td> <td class="quantity-number">25</td> <td>Ghi</td> </tr> <tr> <td class="product-name">Product 4</td> <td class="quantity-number">43</td> <td>Ghi</td> </tr> <tr> <td class="product-name">Product 3</td> <td class="quantity-number">51</td> <td>Ghi</td> </tr> <tr> <td class="product-name">Product 5</td> <td class="quantity-number">15</td> <td>Ghi</td> </tr> <tr> <td class="product-name">Product 3</td> <td class="quantity-number">1</td> <td>Abc</td> </tr> <tr> <td class="product-name">Product 5</td> <td class="quantity-number">59</td> <td>Abc</td> </tr> </tbody> </table> <h4>Total products to be supplied: <span id="total-requests-number"></span> </h4> <div class="container"> <div class="row row-cols-1 row-cols-md-2 justify-content-center row-cols-lg-3 py-4 g-4 counter-1" id="result-total"></div> </div> </body> </html>
Advertisement
Answer
I’m assuming your requirement is to optimize the code to calculate the total for each product and display the total in the overview.
You can start with by getting the product total through looping each row, return an object data with product name as key and value as total e.g. { "Product 1": 10 }
. Following example is achieve by using reduce():
const getProductTotals = (rowElements) => { /* convert to an array to enable for perform reduce function */ const productRows = rowElements.toArray(); /* loop through each product row, and return an object data */ const productTotals = productRows.reduce((data, row) => { /* get prduct name and quantity from row */ const productName = $(row).children('.product-name').first().text(); const productQuantity = parseInt($(row).children('.product-quantity').first().text()); /* initialize a product quantity if not exixst */ if (!data[productName]) { data[productName] = 0; } /* add the quantity to the product name */ data[productName] += productQuantity; return data; }, {}); return productTotals; };
By execute getProductTotals($('#product-list tbody tr'))
, you will get an object data which looks like this, key as product name and value as product total:
{ "Product 1": 20, "Product 2": 100, "Product 3": 1 }
Then render the overview by using the object data. Loop through the object properties which is the product name and total, and append to overview:
/* render the product total overview by an object type data, which return from "getProductTotals" function */ const renderOverview = (data) => { /* select tbody in ID overview table */ const tbody = $('#overview tbody'); /* erase old content */ tbody.html(''); /* loop through the product total data object */ $.each(data, (productName, total) => { /* create the row html */ const html = ` <tr> <td>${productName}</td> <td>${total}</td> </tr> `; /* append the html to the tbody */ tbody.append(html); }); };
Create a sync
function, so every time you add or remove your row execute this function to update the overview:
/* update the overview display by execute this function */ const sync = () => { const productTotals = getProductTotals($('#product-list tbody tr')); renderOverview(productTotals); };
Demo:
/* accept array of elements, using reduce function, return an object with product name as key and total as value */ const getProductTotals = (rowElements) => { /* convert to an array to enable for perform reduce function */ const productRows = rowElements.toArray(); /* loop through each product row, and return an object data */ const productTotals = productRows.reduce((data, row) => { /* get prduct name and quantity from row */ const productName = $(row).children('.product-name').first().text(); const productQuantity = parseInt($(row).children('.product-quantity').first().text()); /* initialize a product quantity if not exixst */ if (!data[productName]) { data[productName] = 0; } /* add the quantity to the product name */ data[productName] += productQuantity; return data; }, {}); return productTotals; }; /* render the product total overview by an object type data, which return from "getProductTotals" function */ const renderOverview = (data) => { /* select tbody in ID overview table */ const tbody = $('#overview tbody'); /* erase old content */ tbody.html(''); /* loop through the product total data object */ $.each(data, (productName, total) => { /* create the row html */ const html = ` <tr> <td>${productName}</td> <td>${total}</td> </tr> `; /* append the html to the tbody */ tbody.append(html); }); }; /* update the overview display by execute this function */ const sync = () => { const productTotals = getProductTotals($('#product-list tbody tr').not('#last-row')); renderOverview(productTotals); }; /* update at first time */ $(document).ready(() => { sync(); }); /* add product to demo the sync */ function addProduct() { const getRandomInt = (max) => { max = Math.floor(max); const min = 1; return Math.floor(Math.random() * (max - min + 1)) + min; }; const randomIndex = getRandomInt(3); const randomQuantity = getRandomInt(100); const html = ` <tr> <td class="product-name">Product ${randomIndex}</td> <td class="product-quantity">${randomQuantity}</td> <td> <button type="button" onclick="removeProduct(this)">Remove</button> </td> </tr> `; $(html).insertBefore('#last-row'); sync(); } /* remove product to demo the sync */ function removeProduct(button) { $(button).closest('tr').remove(); sync(); }
<b>Overview</b> <div id="overview"> <table border="1" cellpadding="5"> <thead> <tr> <th>Product</th> <th>Total</th> </tr> </thead> <tbody> </tbody> </table> </div> <br /> <b>Product List</b> <table id="product-list" border="1" cellpadding="5"> <thead> <tr> <th>Product</th> <th>Quantity</th> <th></th> </tr> </thead> <tbody> <tr> <td class="product-name">Product 1</td> <td class="product-quantity">20</td> <td> <button type="button" onclick="removeProduct(this)">Remove</button> </td> </tr> <tr> <td class="product-name">Product 2</td> <td class="product-quantity">100</td> <td> <button type="button" onclick="removeProduct(this)">Remove</button> </td> </tr> <tr> <td class="product-name">Product 3</td> <td class="product-quantity">1</td> <td> <button type="button" onclick="removeProduct(this)">Remove</button> </td> </tr> <tr id="last-row"> <td colspan="3"> <button type="button" onclick="addProduct()">Add Random Product</button> </td> </tr> </tbody> </table> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>