What are the limitations of sorting by price?

One issue I am facing is that the sorting functionality in the sortProductsByPrice (sortOrder) method doesn't work properly when products are deleted or added. Currently, sorting only applies to products that are already present in the this.products array by default.

(Sorting is triggered by clicking on <th> Price: </ th>).

It is essential for the sorting feature to be able to handle additions and deletions of products as well.

//This is the Product Class
class Product {
    constructor(name, count, price) {
        this.name = name;
        this.count = count;
        this.price = price;
    }
}
Product.SORT_ORDER_ASC = 1;
Product.SORT_ORDER_DESC = -1;
// This is the Shop Class where products are managed
class Shop {
    constructor() {
        this.products = [];
    }

    //method to add a product
    addProduct(newProduct) {
        this.products.push(newProduct);
    }

    //method to remove product by name
    deleteProductByName(productName) {
        let i = this.products.length;
        while (i--) {
            if (productName === this.products[i].name) {
                this.products.splice(i, 1);
            }
        }
    }

    // calculating total price of all products
    get totalProductsPrice {
        return this.products.map(product => product.price).reduce((p, c) => p + c);
    }

    //method to sort products based on their price
    sortProductsByPrice(sortOrder) {
        const sorted = this.products.sort((a, b) => {
            return a.price > b.price ? sortOrder : -sortOrder;
        });
        this.products = sorted;
    }

    // method to render the table with product properties (name, count, price)
show() {
    const formAdd = document.forms[0];
    const inputsAdd = formAdd.elements;
    const buttAdd = formAdd.elements[3];
    const formDelete = document.forms[1];
    const nameDelete = formDelete.elements[0];
    const buttDelete = formDelete.elements[1];
    const priceFilter = document.getElementById("filter");
    // add new product when clicked
    buttAdd.addEventListener('click', (e) => {
        e.preventDefault();
        shop.addProduct(new Product(inputsAdd[0].value, parseInt(inputsAdd[2].value),
            parseInt(inputsAdd[1].value)));
shop.show();
    }, false);
    // delete product by name when clicked
    buttDelete.addEventListener('click', (e) => {
        e.preventDefault();
        shop.deleteProductByName(nameDelete.value);
shop.show();
    }, false);
    const rows = document.querySelectorAll("#shop .data");
    for (let i = rows.length - 1; i >= 0; i--) {
        const e = rows.item(i);
        e.parentNode.removeChild(e);
    }
    const table = document.getElementById("shop");
    const tFoot = table.querySelector('tfoot');
    if (tFoot) tFoot.remove();
    for (let i = 0; i < this.products.length; i++) {
        //create table
        table.innerHTML += `<tbody><tr class="data"><td>${this.products[i].name}</td>
<td>${this.products[i].price}</td>
<td>${this.products[i].count}</td></tr></tbody>`;
    }
    //display total price of all products
    table.innerHTML += `<tfoot><tr><td colspan="3" id="total-price">Total price: 
    ${shop.totalProductsPrice}</td></tr></tfoot>`;

    priceFilter.addEventListener("click", (e) => {
        shop.sortProductsByPrice(Product.SORT_ORDER_ASC);
shop.show();
    }, false);
}
}

let shop = new Shop();
shop.addProduct(new Product("product", 1, 2000));
shop.addProduct(new Product("product1", 2, 500));
shop.addProduct(new Product("product2", 3, 1000));
shop.show();
 
console.log(shop.products);
<div class="Shop">
    <div class="add-product">
        <h1>Add product</h1>
        <form id="addForm">
            <label for="name">Name of product</label>
            <input type="text" id="name" class="input-product">
            <label for="price">Price of product</label>
            <input type="text" id="price" class="input-product">
            <label for="count">Count of product</label>
            <input type="text" id="count" class="input-product">
            <button id="add">Add</button>
        </form>
    </div>
<div class="product-table">
    <h2>Products</h2>
    <form id="delete-form">
        <label for="name-delete">Delete product by name</label>
        <input type="text" id="name-delete" class="input-delete">
        <button id="delete" type="button">Delete</button>
    </form>
    <table id="shop">
        <caption>Products that are available in the store</caption>
        <tr>
            <th>Name:</th>
            <th id="filter">Price:</th>
            <th>Count:</th>
        </tr>
    </table>
</div>
</div>

Answer №1

Ensuring that sorting remains functional even after goods are added or removed is crucial.

The reason the sorting functionality breaks after adding/removing items is because the show() method is called, which redraws the entire HTML without reattaching any event handlers. As a result, the originally attached event handlers become unbound.

To resolve this issue, make sure to attach the event handlers within your show() method.


In your method, there is no need to return the sorted array separately as the original this.products array will be automatically sorted in place.

Hence, you can eliminate the declaration of the sorted array and the return statement, instead simply use this.products.sort() to sort the array.

 sortProductsByPrice(sortOrder) {
    this.products.sort((a, b) => {
      return a.price > b.price ? sortOrder : -sortOrder;
    });
 }

Demo:

//Product Creation Class
class Product {
  constructor(name, count, price) {
    this.name = name;
    this.count = count;
    this.price = price;
  }
}
Product.SORT_ORDER_ASC = 1;
Product.SORT_ORDER_DESC = -1;
// Class where products are recorded
class Shop {
  constructor() {
    this.products = [];
  }

  //method for adding a product
  addProduct(newProduct) {
    this.products.push(newProduct);
  }

  //method for remove product by name
  deleteProductByName(productName) {
    let i = this.products.length;
    while (i--) {
      if (productName === this.products[i].name) {
        this.products.splice(i, 1);
      }
    }
  }

  // get total price by all products
  get totalProductsPrice {
    return this.products.map(product => product.price).reduce((p, c) => p + c);
  }

  //method for sorting the product at its price
  sortProductsByPrice(sortOrder) {
    this.products.sort((a, b) => {
      return a.price > b.price ? sortOrder : -sortOrder;
    });
  }

  // method to draw the table with product property (
  // name, count, price)
  show {
    const rows = document.querySelectorAll("#shop .data");
    for (let i = rows.length - 1; i >= 0; i--) {
      const e = rows.item(i);
      e.parentNode.removeChild(e);
    }
    const table = document.getElementById("shop");
    const tFoot = table.querySelector('tfoot');
    if (tFoot) tFoot.remove();
    for (let i = 0; i < this.products.length; i++) {
      //create table
      table.innerHTML += `<tbody><tr class="data"><td>${this.products[i].name}</td>
    <td>${this.products[i].price}</td>
    <td>${this.products[i].count}</td></tr></tbody>`;
    }
    //show total price by all products
    table.innerHTML += `<tfoot><tr><td colspan="3" id="total-price">Total price: 
        ${shop.totalProductsPrice}</td></tr></tfoot>`;
  }
}
// add new product by click
const formAdd = document.forms[0];
const inputsAdd = formAdd.elements;
const buttAdd = formAdd.elements[3];
buttAdd.addEventListener('click', (e) => {
  e.preventDefault();
  shop.addProduct(new Product(inputsAdd[0].value, parseInt(inputsAdd[2].value),
    parseInt(inputsAdd[1].value)));
  shop.show();
}, false);
// delete product by name after click
const formDelete = document.forms[1];
const nameDelete = formDelete.elements[0];
const buttDelete = formDelete.elements[1];
buttDelete.addEventListener('click', (e) => {
  e.preventDefault();
  shop.deleteProductByName(nameDelete.value);
  shop.show();
}, false);
let shop = new Shop();
shop.addProduct(new Product("product", 1, 2000));
shop.addProduct(new Product("product1", 2, 500));
shop.addProduct(new Product("product2", 3, 1000));
shop.show();
const priceFilter = document.getElementById("filter");
//filter products by price
priceFilter.addEventListener("click", (e) => {
  shop.sortProductsByPrice(Product.SORT_ORDER_ASC);
  shop.show();
}, false);
<div class="Shop">
  <div class="add-product">
    <h1>Add product</h1>
    <form id="addForm">
      <label for="name">Name of product</label>
      <input type="text" id="name" class="input-product">
      <label for="price">Price of product</label>
      <input type="text" id="price" class="input-product">
      <label for="count">Count of product</label>
      <input type="text" id="count" class="input-product">
      <button id="add">Add</button>
    </form>
  </div>
  <div class="product-table">
    <h2>Products</h2>
    <form id="delete-form">
      <label for="name-delete">Delete product by name</label>
      <input type="text" id="name-delete" class="input-delete">
      <button id="delete" type="button">Delete</button>
    </form>
    <table id="shop">
      <caption>Products that are available in the store</caption>
      <tr>
        <th>Name:</th>
        <th id="filter">Price:</th>
        <th>Count:</th>
      </tr>
    </table>
  </div>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error when attempting to open and close a div through a click event

Currently, I am in the process of developing a web service that generates arrays. I am facing an issue where all the div elements on my page load open by default, but I want them to remain closed and only open upon clicking. Specifically, I want a nested d ...

Trigger a function in JavaScript/jQuery after an event has finished executing and has provided a return value

Is there a way in JavaScript/jQuery to execute a function after all callback handlers following a keyDown event have finished? I have noticed that checking the value of a form input box immediately after the keyDown event returns an empty value, but when I ...

Exploring the World of Vue.js Object Imports

I am currently encountering an issue involving the importing of Objects from App.vue into a component. To provide context, this project consists of a Navigation Drawer component and an App.vue file. The Navigation Drawer contains Vue props that can be dyna ...

What is the best way to send an object from the front end to the backend using res.send?

In my current project, I am establishing communication between the frontend and backend. The process involves the backend sending an expression to the frontend for computation, after which the computed answer needs to be sent back to the backend. For exam ...

Stop the form from submitting when the enter key is pressed using VueJS and Semantic UI

After spending the past two days searching for a solution to this persistent issue, none of the suggested remedies have proven effective so far. My form's HTML structure is as follows: <form id="quote_form" action="" method="get" class="ui large ...

Use Google script to input drop down options directly into a Google spreadsheet

After gathering coding advice from various StackOverflow examples, I've hit a roadblock while attempting to take my script to the next level. The task seems simple enough, but for some reason, I just can't figure it out. Here's the situati ...

Dnd-kit: item loses its place in line when dragged over Droppable area

I've encountered an issue while using @dnd-kit. The sorting function works smoothly (e.g. when I drag the 1st element, it sorts correctly), but once I reach a droppable element (green Thrash), the sorting breaks and the 1st element snaps back. You ca ...

What is the most effective way to assign multiple functions to the onClick event of a button, based on a specific property,

I have a special button that generates a specific type of code when rendered: <button>{this.props.text}</button> This button is named ButtonCustom. In the render method of the main container, I am trying to achieve the following: function my ...

The direction of view decides the reversal of Three.js Materials

Working on my latest project using Three.js to create a virtual model of a house. The main purpose is to showcase the design to an architect for further discussion. The interactive aspect allows users to walk through the home virtually on this website. To ...

Unexpected disconnection from Socket.io server

Utilizing node.js service and angular client with socket.io for extended duration http requests. Service: export const socketArray: SocketIO.Socket[] = []; export let socketMapping: {[socketId: string]: number} = {}; const socketRegister: hapi.Plugin< ...

There seems to be an issue with the babel `--watch` feature, as it is not properly updating the es6 folder

I am in the process of developing a basic component library and I want it to be automatically compiled every time I make any changes to my files. Here is the command I am currently using: "watch": "babel components/ --out-dir dist --copy-files --ignore t ...

The "checked" property is absent in the ASP.NET checkbox within the gridview

In my asp.net gridview control, the checked property seems to be missing. I am trying to access the checked property using jquery Gridview structure: <Columns> <asp:TemplateField> <ItemTemplate> ...

Exploring a personalized approach to searching in DataTables

I am facing a dilemma with implementing a custom search in a DataTables table due to my limited experience with JavaScript. The function I have only triggers on startup and not when typing in the search box. If you're interested, you can find my orig ...

"Implementing angularJS html5mode within a controller: A step-by-step guide

Recently, I have been trying to familiarize myself with AngularJS. I'm attempting to set the html5 mode to true in a specific controller within my AngularJS application which consists of three controllers. Despite my efforts to configure the setting u ...

Show occurrences of an array categorized by date using JSON format

I'm interested in analyzing a JSON array to find the occurrences of a specific item by date. Let me demonstrate with the following JSON example: "data": [ { "tags": [ "foo", "bar", "hello", "world", " ...

Embed a Vaadin component within an element dynamically generated by a Javascript component

I am currently designing a Vaadin application and working on a custom Javascript component, which is a subclass of AbstractJavascriptComponent. This component uses jQuery to generate a table-like structure. In certain scenarios, users should be able to in ...

Remove an option from a drop-down menu once a different option has been selected (using Jquery)

I need to remove an item (ItemX) from a dropdown menu (Drop Down 2) when ItemX is selected in Drop Down 1 using jQuery. This is how I am approaching it: <?php session_start (); ?> <!doctype html> <html lang="en"> <head> <ti ...

Setting up package.json to relocate node_modules to a different directory outside of the web application:

My web app is currently located in C:\Google-drive\vue-app. When I run the command yarn build, it installs a node_modules folder within C:\Google-drive\vue-app. However, since I am using Google Drive to sync my web app source code to Go ...

Tips for invoking an Android function from an AngularJS directive?

I am facing an issue with my HTML that uses an AngularJS directive. This HTML file is being used in an Android WebView, and I want to be able to call an Android method from this directive (Learn how to call Android method from JS here). Below is the code ...

Menu Toggle with Bootstrap 3

I have implemented two Bootstrap 3 navigation menus on a single page. One menu works fine when the toggle button is activated for responsiveness, but the other one doesn't work as expected. How can I resolve this issue? Below are the code snippets fo ...