3

I have the following JSON array I want to create a new field in every object which will be a count of the object

we have to get a count based on status, shop, and name(ownerDetails)

How can I achieve this and I have added my expected output below

 var 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":[ ]
  }
] 

Expected output: So based on each shop, status and name(ownerDetails) we have to count the object

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

Plese see demo

Thanks @Nico_ for your and @Parth Ravalhelp

Below code is not working when "ownerDetails":[ ] and I got the below error Cannot read properties of undefined (reading 'name') in console

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)
2
  • Have you attempted anything & if yes, would you please share it? If not, please try using new Map() to construct a map whose key will be a combination of status and Shop and the value will be a count. Using it, you may transform your current array so that each object will have a count. Commented May 2, 2022 at 9:38
  • Please show your code so far. Commented May 5, 2022 at 18:01

4 Answers 4

2
+25

Deconstruction of a not defined object

/**
 * Problem: The deconstructed object is not defined.
 *
 * This will throw: TypeError: Cannot read properties of undefined (reading 'name')
 * 
 * This is roughly equivalent of doing const { name } = undefined
 */
const [{ name }] = [];

/**
 * Solution: Assign a default value when the deconstructed object is not defined.
 *
 * If the deconstructing object is undefined,
 * assign an empty object as the default value
 */
const [{ name } = {}] = [];

Accessing a property of an undefined object

If you don't check that the ownerDetails array isn't empty before comparing item.ownerDetails[0].name === name, you might end up trying to access a property of an object that doesn't exist. This will result in a TypeError like above.

To deal with this situation, you must:

  1. Verify that there is at least one element in item.ownerDetails;
  2. Verify that name property exists on the first element;

If the tests passed, returns the value of the name property for the first element of ownerDetails. Otherwise, you must provide a default value.

Working example

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,
   * assign 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. Accessing and returning name property.
         */
        item.ownerDetails[0].name
        /**
         * Else, compare undefined with name property.
         */
      ) || undefined
    ) === name
  ).length
}));

console.log(itemsWithCount)

Output

[
  { 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' }
]
Sign up to request clarification or add additional context in comments.

Comments

1

var 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"
    }]
  }
];

var mapData = items.map((data) => {
  var getList = items.filter(word => word.Shop == data.Shop).length;
  return {
    id: data.id,
    status: data.status,
    Shop: data.Shop,
    text: data.ownerDetails,
    Count: getList
  };
});

console.log(mapData);

Note:- Map Data and Counted Similar Shops....

2 Comments

"ownerDetails":[ ] and I got the below error Cannot read properties of undefined (reading 'name') in console.could you please help.
this is the working example here, what is the issue you are facing. can you please put the fiddle example, so, i can solve that issue
1

Looping through the array for each element is not an efficient way to go about it. A cleaner and more efficient way would be to create an object with keys as the combination of the values you need to compare.

Following should help:

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 would not work if the order of the objects is to be preserved

Comments

0

You can do it with a map to loop through your array (you need to add some coma before the "Shop" by the way.

  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
    }));

For each item of your array, you keep the current value of the item (the ...item) and you add a Count property, that will get the count for this item by filtering the array to keep only the item that have the same status and shop and you get the length of that array.

12 Comments

it's not working when I try to add one more check. please have a look jsfiddle.net/wL46so8a
You need to first understand how the above solution works in order to enhance/extend it to handle another nested sub-array named ownerDetails. Simply adding "name" to the de-structuring along with { status, Shop } will not get the name from the array ownerDetails into the variable name. The proper way to de-structure with ownerDetails may be something like so: { status, Shop , ownerDetails: [{ name }]}. Again, this will only pick the name from the object at index 0. If there are more elements in the ownerDetails array - it will ignore those.
got following error Cannot read properties of undefined (reading 'name') when "ownerDetails":[ ] is empty array .jsfiddle.net/8qgtsaz6/1
It's because you are trying to read the first item of an empty array, so you have to check first the array is not empty : const itemsWithCount = items.map(item => ({ ...item, Count: items.filter(({ status, Shop ,ownerDetails: [{ name }]}) => item.status === status && item.Shop === Shop && item.ownerDetails.length && item.ownerDetails[0].name === name).length }));
your code is not working, still getting the same error in ownerDetails: [{ name }]
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.