Home > Enterprise >  how to filter by ObjectId and calculate total cost in react.js
how to filter by ObjectId and calculate total cost in react.js

Time:12-03

The table is rendered twice for Wade Ivan. Instead, the table should be rendered only once. The total should be 180 and not 680.

I have a search class component. However, when i search for clients by their names based on ObjectId(clientdetails._id), rendering returns multiple tables equivalent to the number of documents with that ObjectId.Moreover, the total bill calculated gives the total cost from all objectIds instead of the total cost derived from documents with similar ObjectId (clientdetails._id)

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const isSearched = searchTerm => item => {
  if (searchTerm === "") {
    return;
  }
  return item.clientdetails.fullName.toLowerCase().includes(searchTerm.toLowerCase());
};

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      list:[
        {
          "_id": "6389d19a3b28b9e4af3694f8",
          "clientdetails": {
            "_id": "6389d1193b28b9e4af3694f2",
            "fullName": "WADE IVAN",
            "dateofBirth": "1990-01-01T00:00:00.000Z",
            "age": 32,
            "__v": 0
          },
          "paymentReferenceCode": "1",
          "paymentType": "Cash",
          "paymentDescription": "ACCOMODATION",
          "procedureAmount": "100",
          "paymentDiscount": 20,
          "AmountPaid": 100,
          "Total": 100,
          "__v": 0
        },
        {
          "_id": "6389d1bb3b28b9e4af369500",
          "clientdetails": {
            "_id": "6389d1193b28b9e4af3694f2",
            "fullName": "WADE IVAN",
            "dateofBirth": "1990-01-01T00:00:00.000Z",
            "age": 32,
            "__v": 0
          },
          "paymentReferenceCode": "12",
          "paymentType": "Cash",
          "paymentDescription": "ACCOMODATION",
          "procedureAmount": "100",
          "paymentDiscount": 20,
          "AmountPaid": 80,
          "Total": 80,
          "__v": 0
        },
        {
          "_id": "6389d1e03b28b9e4af369508",
          "clientdetails": {
            "_id": "6389d0ed3b28b9e4af3694ee",
            "fullName": "JOHN MARK",
            "dateofBirth": "2000-01-12T00:00:00.000Z",
            "age": 22,
            "__v": 0
          },
          "paymentReferenceCode": "3",
          "paymentType": "Cash",
          "paymentDescription": "100",
          "procedureAmount": "100",
          "paymentDiscount": 20,
          "AmountPaid": 200,
          "Total": 200,
          "__v": 0
        },
        {
          "_id": "6389d2173b28b9e4af369510",
          "clientdetails": {
            "_id": "6389d0ed3b28b9e4af3694ee",
            "fullName": "JOHN MARK",
            "dateofBirth": "2000-01-12T00:00:00.000Z",
            "age": 22,
            "__v": 0
          },
          "paymentReferenceCode": "9",
          "paymentType": "Cash",
          "paymentDescription": "ACCOMODATION",
          "procedureAmount": "10",
          "paymentDiscount": 2,
          "AmountPaid": 300,
          "Total": 300,
          "__v": 0
        }
      ],
      searchTerm: ""
    };
  }

  onSearchChange = event => {
    this.setState({ searchTerm: event.target.value });
  };

  render() {
    const { searchTerm, list } = this.state;
    
    return (
      <div className="page">
        <div className="interactions">
          <Search 
            value={searchTerm} 
            SearchChange={this.onSearchChange}
          >
            Search
          </Search>
          <Table list={list} pattern={searchTerm} onDismiss={this.onDismiss} />
        </div>
      </div>
    );
  }
}
const Search = ({ value, SearchChange, children }) => {
  return (
    <form>
      {children}
      <input type="text" value={value} onChange={SearchChange} />
    </form>
  )
}


class Table extends React.Component {
  render() {
    const { list, pattern } = this.props;
    const total=(list.reduce((total,item) =>  total = total   item.AmountPaid , 0 ));
    return (
      <div className="table">
        {list.filter(isSearched(pattern)).map(item => (
          <div key={item.clientdetails.fullName} className="table-row" >
<table>
                <thead>
                <tr>
                    <th>Client Name </th>
                    <th>Payment Reference Code</th>
                    <th>Payment Type</th>
                    <th>Payment Description</th>
                    <th>Procedure Amount</th>
                    <th>Payment Discount </th>
                    <th>Amount Paid </th>
                    
                </tr>
            </thead>
            <tbody>
            {list.filter(isSearched(pattern)).map(item => (
                    <tr > 
                        <td>{item.clientdetails.fullName}</td>

                        <td>{item.paymentReferenceCode}</td>
                        <td>{item.paymentType}</td>
                        <td>{item.paymentDescription}</td>
                        <td>{item.procedureAmount}</td>
                        <td>{item.paymentDiscount}</td>
                        <td>{item.AmountPaid}</td>
                       
                       
                    </tr>
                    ))}
                   <tfoot><tr>
                        Total: 
                             {total}
                           </tr>  
                    </tfoot>
            </tbody>
        </table>
          </div>
        ))}
      </div>
    );
  } 
}

How can i improve this code. Thanks in advance

CodePudding user response:

Here is the basic solution

let items = [
  {
    _id: "6389d19a3b28b9e4af3694f8",
    clientdetails: {
      _id: "6389d1193b28b9e4af3694f2",
      fullName: "WADE IVAN",
      dateofBirth: "1990-01-01T00:00:00.000Z",
      age: 32,
      __v: 0
    },
    paymentReferenceCode: "1",
    paymentType: "Cash",
    paymentDescription: "ACCOMODATION",
    procedureAmount: "100",
    paymentDiscount: 20,
    AmountPaid: 100,
    Total: 100,
    __v: 0
  },
  {
    _id: "6389d1bb3b28b9e4af369500",
    clientdetails: {
      _id: "6389d1193b28b9e4af3694f2",
      fullName: "WADE IVAN",
      dateofBirth: "1990-01-01T00:00:00.000Z",
      age: 32,
      __v: 0
    },
    paymentReferenceCode: "12",
    paymentType: "Cash",
    paymentDescription: "ACCOMODATION",
    procedureAmount: "100",
    paymentDiscount: 20,
    AmountPaid: 80,
    Total: 80,
    __v: 0
  },
  {
    _id: "6389d1e03b28b9e4af369508",
    clientdetails: {
      _id: "6389d0ed3b28b9e4af3694ee",
      fullName: "JOHN MARK",
      dateofBirth: "2000-01-12T00:00:00.000Z",
      age: 22,
      __v: 0
    },
    paymentReferenceCode: "3",
    paymentType: "Cash",
    paymentDescription: "100",
    procedureAmount: "100",
    paymentDiscount: 20,
    AmountPaid: 200,
    Total: 200,
    __v: 0
  },
  {
    _id: "6389d2173b28b9e4af369510",
    clientdetails: {
      _id: "6389d0ed3b28b9e4af3694ee",
      fullName: "JOHN MARK",
      dateofBirth: "2000-01-12T00:00:00.000Z",
      age: 22,
      __v: 0
    },
    paymentReferenceCode: "9",
    paymentType: "Cash",
    paymentDescription: "ACCOMODATION",
    procedureAmount: "10",
    paymentDiscount: 2,
    AmountPaid: 300,
    Total: 300,
    __v: 0
  }
];

const reduced = items.reduce((acc, curr) => {
  //console.log(curr);
  const prop = curr.clientdetails._id;
 const existingTotal = acc?.[prop]?.total ?? 0;
  return {
    ...acc,
    [prop]: {
      id: prop,
      name: curr.clientdetails.fullName,
      total: existingTotal   curr.Total
    }
  };
}, {});

const list = Object.entries(reduced).map(([key, value]) => {
  return {
    id: key,
    name: value.name,
    total: value.total
  };
});

console.log("reduced", reduced);
console.log("list", list);

To include it in component

// if only final total is needed, this function can be optimized
// if grouping is needed then total needs to be gropued for each entry
function getTotal(items) {
  try {

      const reduced = items?.reduce((acc, curr) => {
      const prop = curr.clientdetails._id;
      const existingTotal = acc?.[prop]?.total ?? 0;
    
      console.log(acc, acc?.[prop]);
      console.log(acc, curr.clientdetails.fullName);
    
      return {
        ...acc,
        [prop]: {
          id: prop,
          name: curr.clientdetails.fullName,
          total: existingTotal   curr.Total
        }
      };
    }, {});

    const total = Object.entries(reduced).reduce((acc, [key, value]) => {
     
      return acc   value.total 
     
    }, 0);

    return total;
    
  } catch (error) {
    console.log("error", error);
    return 0;
  }
}

// for grouping filtered items
function groupItems(items) {
  try {
    const grouped = items?.reduce((acc, curr) => {
      const prop = curr.clientdetails._id;
      //const id = curr.clientdetails._id;

      const existingTotal = acc?.[prop]?.total ?? 0;

      let entries = acc[prop]?.entries ?? [];

      entries.push({
        id: curr._id,
        total: curr.Total,
        paymentType: curr.paymentType
        // include more props
      });

      return {
        ...acc,
        [prop]: {
          name: curr.clientdetails.fullName,
          total: existingTotal   curr.Total,
          entries
        }
      };
    }, {});

    console.log("list", grouped);

    const list = Object.entries(grouped).map(([key, value]) => {
      return {
        id: key,
        name: value.name,
        total: value.total,
        entries: value.entries
      };
    });

    return list;
  } catch (error) {
    console.log("error", error);
    return [];
  }
}

/*
will contain list of
{
    id: key,
    name: value.name,
    total: value.total,
    // contains individual entries and the total for the grouped entries
    entries: [
      {
         id: curr._id,
         total: curr.Total,
         paymentType: curr.paymentType
      }
    ]

}
*/

class Table extends React.Component {

  render() {

    const { list, pattern } = this.props;

    const filteredList = list.filter(isSearched(pattern));

    const grouped = groupItems(filteredList);
   /*
    will contain list of
   {
    id: key,
    name: value.name,
    total: value.total,
    // contains individual entries and the total for the grouped entries
    entries: [
      {
         id: curr._id,
         total: curr.Total,
         paymentType: curr.paymentType
      }
    ]

    }
   */
    // TODO
    // you can write function to get total from grouped items

    const total = getTotal(filteredList)

    return (
      <div className="table">
        //you can show the gropued entries here
        {grouped?.map(({ id, total: grpTotal, entries = [] }) => (
          <div key={item.id} className="table-row" >

This code an be optimized for performance and made simpler with functional components.

Hope it helps

  • Related