How to tally items according to their status and store utilizing JavaScript?

I have a JSON array and I am looking to add a new field to each object which will represent the count of that object.

The count should be based on the status, shop, and name in the ownerDetails property.

How can I achieve this? The expected output is provided below.

 var items = [ 
// JSON data here 
]

Expected output: The objects should have an additional "Count" field representing the count based on status, shop, and name(ownerDetails).

    [
  // Updated JSON data with Count field added 
] 

Check out the demo for more information.

Special thanks to @Nico_ and @Parth Raval for their assistance.

The code snippet provided encounters an error when "ownerDetails":[ ] causing 'Cannot read properties of undefined (reading 'name')' issue in the console.

Here's the problematic code:

const itemsWithCount = items.map(item => ({
  ...item,
   Count: items.filter(({ status, Shop ,ownerDetails: [{ name }]}) => item.status === status && item.Shop === Shop && item.ownerDetails[0].name === name).length
}));

console.log(itemsWithCount)

Answer №1

Dissecting an Undefined Object

/**
 * Issue: Attempting to deconstruct an undefined object.
 *
 * Result: Throws TypeError: Cannot read properties of undefined (reading 'name')
 * 
 * Equivalent to const { name } = undefined
 */
const [{ name }] = [];

/**
 * Resolution: Set a default value when deconstructing an undefined object.
 *
 * If the object being deconstructed is undefied,
 * assign an empty object as the default value
 */
const [{ name } = {}] = [];

Accessing Properties from an Undefined Object

If you fail to verify that the ownerDetails array is not empty before comparing

item.ownerDetails[0].name === name
, you may encounter errors trying to access non-existent object properties. This can lead to a Type Error as mentioned above.

To handle such scenarios, it is essential to:

  1. Ensure there is at least one element in item.ownerDetails;
  2. Confirm the existence of the name property on the first element;

If the tests pass, return the value of the name property for the first element in ownerDetails. Otherwise, provide a default value.

Example in Action

const items = [
  { id: 1, status: 'ORANGE', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
  { id: 2, status: 'GREEN', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
  { id: 3, status: 'ORANGE', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
  { id: 4, status: 'YELLOW', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
  { id: 5, status: 'RED', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
  { id: 6, status: 'GREEN', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
  { id: 7, status: 'GREEN', Shop: 'XYZ', ownerDetails: [{ name: 'test2', address: 'test2' }] },
  { id: 8, status: 'ORANGE', Shop: 'XYZ', ownerDetails: [{ name: 'test2', address: 'test2' }] },
  { id: 9, status: 'YELLOW', Shop: 'ABC', ownerDetails: [{ name: 'test1', address: 'test1' }] },
  { id: 10, status: 'GREEN', Shop: 'EFG', ownerDetails: [{ name: 'test3', address: 'test3' }] },
  { id: 11, status: 'GREEN', Shop: 'EFG', ownerDetails: [] }
];

const itemsWithCount = items.map(item => ({
  ...item,
  /**
   * If the first element of ownerDetails is not defined,
   * set an empty object as the default value for the first
   * element of ownerDetails array.
   */
  Count: items.filter(({ status, Shop, ownerDetails: [{ name } = {}] }) =>
    item.status === status &&
    item.Shop === Shop &&
    (
      (
        /**
         * 1. Check if ownerDetails is not empty.
         */
        item.ownerDetails.length &&
        /**
         * 2. Check if the first element of item.ownerDetails has a name property.
         *
         * ESLint best practice:
         * ------------------------------------------------------
         * Disallow calling some Object.prototype methods directly on objects.
         * For more details, see https://eslint.org/docs/rules/no-prototype-builtins
         * ------------------------------------------------------
         */
        Object.prototype.hasOwnProperty.call(item.ownerDetails[0], 'name') &&
        /**
         * 3. Access and return name property.
         */
        item.ownerDetails[0].name
        /**
         * Else, compare undefined with name property.
         */
      ) || undefined
    ) === name
  ).length
}));

console.log(itemsWithCount)

Output Results

[
  { Count: 2, id: 1, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'ORANGE' },
  { Count: 2, id: 2, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'GREEN' },
  { Count: 2, id: 3, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'ORANGE' },
  { Count: 2, id: 4, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'YELLOW' },
  { Count: 1, id: 5, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'RED' },
  { Count: 2, id: 6, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'GREEN' },
  { Count: 1, id: 7, ownerDetails: [{ address: 'test2', name: 'test2' }], Shop: 'XYZ', status: 'GREEN' },
  { Count: 1, id: 8, ownerDetails: [{ address: 'test2', name: 'test2' }], Shop: 'XYZ', status: 'ORANGE' },
  { Count: 2, id: 9, ownerDetails: [{ address: 'test1', name: 'test1' }], Shop: 'ABC', status: 'YELLOW' },
  { Count: 1, id: 10, ownerDetails: [{ address: 'test3', name: 'test3' }], Shop: 'EFG', status: 'GREEN' },
  { Count: 1, id: 11, ownerDetails: [], Shop: 'EFG', status: 'GREEN' }
]

Answer №2

const products = [{
    "id": 1,
    "status": "ORANGE",
    "Shop": "ABC",
    "ownerDetails": [{
      "name": "test1",
      "address": "test1"
    }]

  },
  {
    "id": 2,
    "status": "GREEN",
    "Shop": "ABC",
    "ownerDetails": [{
      "name": "test1",
      "address": "test1"
    }]
  },
  {
    "id": 3,
    "status": "ORANGE",
    "Shop": "ABC",
    "ownerDetails": [{
      "name": "test1",
      "address": "test1"
    }]
  },
  {
    "id": 4,
    "status": "YELLOW",
    "Shop": "ABC",
    "ownerDetails": [{
      "name": "test1",
      "address": "test1"
    }]
  },
  {
    "id": 5,
    "status": "RED",
    "Shop": "ABC",
    "ownerDetails": [{
      "name": "test1",
      "address": "test1"
    }]
  },
  {
    "id": 6,
    "status": "GREEN",
    "Shop": "ABC",
    "ownerDetails": [{
      "name": "test1",
      "address": "test1"
    }]
  },
  {
    "id": 7,
    "status": "GREEN",
    "Shop": "XYZ",
    "ownerDetails": [{
      "name": "test2",
      "address": "test2"
    }]
  },
  {
    "id": 8,
    "status": "ORANGE",
    "Shop": "XYZ",
    "ownerDetails": [{
      "name": "test2",
      "address": "test2"
    }]
  },
  {
    "id": 9,
    "status": "YELLOW",
    "Shop": "ABC",
    "ownerDetails": [{
      "name": "test1",
      "address": "test1"
    }]
  },
  {
    "id": 10,
    "status": "GREEN",
    "Shop": "EFG",
    "ownerDetails": [{
      "name": "test3",
      "address": "test3"
    }]
  }
];

const shopData = products.map((data) => {
  const count = products.filter(item => item.Shop == data.Shop).length;
  return {
    id: data.id,
    status: data.status,
    Shop: data.Shop,
    text: data.ownerDetails,
    Count: count
  };
});

console.log(shopData);

Note:- Mapped Data with Count of Similar Shops....

Answer №3

Using a more optimized approach instead of looping through the array for each element is recommended. To achieve this, consider creating an object with keys representing the combination of values to be compared.

Here's a helpful snippet:

let items = [
  {"id":1,"status":"ORANGE","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
  {"id":2,"status":"GREEN","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
  {"id":3,"status":"ORANGE","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
  {"id":4,"status":"YELLOW","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
  {"id":5,"status":"RED","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
  {"id":6,"status":"GREEN","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
  {"id":7,"status":"GREEN","Shop":"XYZ","ownerDetails":[{"name":"test2","address":"test2"}]},
  {"id":8,"status":"ORANGE","Shop":"XYZ","ownerDetails":[{"name":"test2","address":"test2"}]},
  {"id":9,"status":"YELLOW","Shop":"ABC","ownerDetails":[{"name":"test1","address":"test1"}]},
  {"id":10,"status":"GREEN","Shop":"EFG","ownerDetails":[{"name":"test3","address":"test3"}]},
  {"id":11,"status":"GREEN","Shop":"EFG","ownerDetails":[]}
]

    let itemMap = {};

    for (const item of items) {
      const key = `${item.Shop}_${item.status}_${item.ownerDetails[0]?.name}`;
      if (itemMap[key]) {
          itemMap[key].push({ ...item, Count: itemMap[key][0].Count+1 });
          for (const matchedItem of itemMap[key]) {
              matchedItem.Count++;
          }
      } else {
          itemMap[key] = [{ ...item, Count: 1 }];
      }
    }

    let processedItems = [];
    for (const items of Object.values(itemMap)) {
        for (const item of items) {
             processedItems.push(item);
        }
    }
    
    console.log(processedItems);

Note: This method may not preserve the order of objects in the array.

Answer №4

You have the ability to achieve this task using a map function to iterate through your array (don't forget to add a comma before the "Shop").

  const items = [
  {
    "id": 1,    
    "status": "ORANGE",
    "Shop":"ABC",
    "ownerDetails":[ {"name":"test1","address":"test1"}]    
    
  },
  {
    "id": 2,
    "status": "GREEN",
    "Shop":"ABC",
    "ownerDetails":[ {"name":"test1","address":"test1"}]
  },
  {
    "id": 3,
    "status": "ORANGE",
    "Shop":"ABC",
    "ownerDetails":[ {"name":"test1","address":"test1"}]    
  },
  {
    "id": 4,
    "status": "YELLOW",
    "Shop":"ABC",
    "ownerDetails":[ {"name":"test1","address":"test1"}]
  },
  {
    "id": 5,
    "status": "RED",
    "Shop":"ABC",
    "ownerDetails":[ {"name":"test1","address":"test1"}]
  },
  {
    "id":6,
    "status": "GREEN",
    "Shop":"ABC",
    "ownerDetails":[ {"name":"test1","address":"test1"}]
  },
  {
    "id": 7,
    "status": "GREEN",
    "Shop":"XYZ",
    "ownerDetails":[ {"name":"test2","address":"test2"}]
  },
   {
    "id": 8,
    "status": "ORANGE",
    "Shop":"XYZ",
    "ownerDetails":[ {"name":"test2","address":"test2"}]    
  },
  {
    "id": 9,
    "status": "YELLOW",
    "Shop":"ABC",
    "ownerDetails":[ {"name":"test1","address":"test1"}]
  },
  {
    "id": 10,
    "status": "GREEN",
    "Shop":"EFG",
    "ownerDetails":[ {"name":"test3","address":"test3"}]
  },
{
    "id": 11,
    "status": "GREEN",
    "Shop":"EFG",
    "ownerDetails":[ ]
  }
];
    
    const itemsWithCount = items.map(item => ({
      ...item,
      Count: items.filter(({ status, Shop }) => item.status === status && item.Shop === Shop).length
    }));

In each iteration of your array, you retain the current value of the item (using ...item) and introduce a new property called Count that calculates the occurrence count by filtering items with matching status and shop, then retrieving the length of the filtered array.

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

Learn how to efficiently transfer row data or an array object to a component within Angular by utilizing the MatDialog feature

My goal is to create a functionality where clicking on a button within a specific row opens up a matDialog box displaying all the contents of that row. Below is the HTML snippet: <tr *ngFor="let u of users"> <td data-label="ID& ...

Explore the capabilities of Chart JS integration using Python Selenium

I've been attempting to click the buttons on this Chart JS located on the webpage . Despite trying by xpath, full xpath, and JS path, I have not been successful. An example of my attempt to press the "All" button can be seen below: https://i.sstatic.n ...

What are the key steps in pinpointing and saving the central positions of dots within a grid?

I am currently seeking advice on the next steps and how to proceed with my project. Here is some context: The task at hand involves finding the center of circles within a grid placed on an image that was originally 1260x683 pixels. The grid consists of 27 ...

Preserving the selected date in a textbox using jQuery DatePicker after a postback

How to Save DatePicker Date in a Textbox after a Postback $(function () { $("#Arrival").datepicker({ minDate: 0, dateFormat: 'dd/mm/yy', showOn: "button", buttonImage: "img/ui_cal_icon.gif", buttonIm ...

"Material-Table does not have the ability to insert a new row

Just started learning JS and React. I'm attempting to integrate Material-table with an add row button, but encountering issues where the added row is not reflecting. Every time I refresh the page, the rows are reset. I suspect there's a problem w ...

What causes useEffect to be triggered multiple times, even when it is dependent on another useEffect function that is being executed repeatedly?

I have a specific requirement that involves using two useEffect hooks. First useEffect is used to set the latitude and longitude in the state with an empty dependency array. useEffect(() => { navigator.geolocation.getCurrentPosition(geo = ...

Discovering the current time and start time of today in EST can be achieved by utilizing Moment.js

Need help with creating Start and End Time stamps using Moment.js in EST: Start Time should reflect the beginning of today End Time should show the current time. This is how I have implemented it using moment.js: var time = new Date(); var startTime=D ...

Steps for creating an HTML report using Intern JS

Our team relies on intern JS for automating functional tests, however we are facing difficulty in generating an html report. I attempted to use locvhtml as suggested by the Intern documentation (https://theintern.github.io/intern/#reporter-lcov), but unfo ...

What's the best way to ensure that only the most recent 100 entries are retained in a CSV file

Currently, I am working on an application that requires me to extract timestamp-based parameter values from various devices. The data is highly structured and because I have to retrieve 100k rows every few minutes, I haven't explored the option of usi ...

Should the space be printed in the middle or at the end in the for loop?

I'm currently working on a program where I need to determine whether spaces should be printed or not. The issue I'm facing is that my code is printing spaces at the end, which is not the desired outcome. Here is a snippet of my code: void functi ...

Produce a vue attribute

I am looking to display a property in my component. My template includes: <v-flex v-for="c in components"> <component :is="c.component" v-bind="c.prop"></component> </v-flex> And in the script: ... mounted(){ ...

Looking for a more efficient method to pass components with hooks? Look no further, as I have a solution ready for

I'm having trouble articulating this query without it becoming multiple issues, leading to closure. Here is my approach to passing components with hooks and rendering them based on user input. I've stored the components as objects in an array an ...

What occurs to a PHP class once it has been initialized?

Exploring PHP and curious about what happens when creating an object from a class in another script. Will there be limitations on manipulating it from a different script once processed? I'm focused on building a deck of cards with unique data for eac ...

A comprehensive guide on creating translation files using grunt angular-translate from original JSON files containing translations

I have a unique angular application that requires support for multiple languages. To achieve this, I have implemented the angular translate task in the following manner. My goal is to create separate language files which can be loaded later using the useSt ...

Tips for choosing all elements in a form that have a specific class assigned

I am attempting to target all fields in a form with a specific class name and then select all the remaining fields. This is my form: <form style="margin:20px 0" id="myform_2"> <p> Query Name : <input i ...

Are there numerous instances of jQuery references both preceding and following the use of '$.noConflict'?

Managing a large project with thousands of PHP files can be challenging, especially when dealing with conflicting jQuery references that hinder the implementation of a desired plugin. Consider this scenario: <html> <head> ...

Converting to JSON: "Property or method not supported by object"

Having a JavaScript Class with a method, but encountering an error when trying to call it toJSON. The reason behind this error remains a mystery to me. try { if (this.TabloEkId == undefined || this.TabloEkId == "") { throw errEksikVeri; } ...

What steps do I need to take in order to transfer these codes from AngularJS to Angular?

I am currently working with the code snippet below: var secClassesToCreate = _this.$filter('filter')(_this.selectedSecurityClasses, {operation: 'CREATE'}); var selectedSecClasses = _this.$filter('filter')(_this ...

Is it possible to inject both a service and a constant into the same controller?

In my controller, I am attempting to import a constant named myConstant from a separate file, as well as a service named myService: app.controller('myController', ["$scope", "v3_0_0", function($scope, myConstant, myService, $ionicPopup, $timeout ...

Having trouble retrieving the JSON value as it returns undefined

Having trouble extracting specific values from JSON data. I'm trying to retrieve the value of result.datafeed[0].prod[0].vertical[0].deviceProductJson[0].product_brand, but it keeps returning as undefined. To investigate further, I examined the stru ...