0

I have an array orders that are objects of orders made. I want to make a report out of this and create a way to remove any duplicates based on id but would like to keep the data of bookId and count in a new array. I also am attempting to add the counts together if multiple counts exist for the same id.

I am having issues achieving this and not sure which direction to take.

  • First I have orderList which is an array of all bookIds, count and name

  • Second I tried using new Set to remove any duplicates from orders

  • Third I am try add orders if the the userIds from orderList and orders are a match.

Here is an example of my snippet:

    const orders = [
        {
            "userId": 1,
            "name": "Person 1",
            "status": "active",
            "bookId": 24,
            "count": 1
        }, 
            {
            "userId": 1,
            "name": "Person 1",
            "status": "active",
            "bookId": 25,
            "count": 2
        }, 
        {
            "userId": 2,
            "name": "Person 2",
            "status": "active",
            "bookId": 8,
            "count": 2
        }, {
            "userId": 1,
            "name": "Person 1",
            "status": "active",
            "bookId": 24,
            "count": 3
        }, {
            "userId": 3,
            "name": "Person 3",
            "status": "active",
            "bookId": 99,
            "count": 1
        }
    ]
    
  
const orderList = orders.map(item => ({count: item.count, id: item.bookId,  userId: item.userId}))


const uniqueOrder = [...new Set(orders)]

let results = orderList.map(item => ({ ...item,
  orders: uniqueOrder.filter(f => f.userId == item.userId)
}));

console.log(results)

Here is an example of the orders array:

const orders = [
    {
        "userId": 1,
        "name": "Person 1",
        "status": "active",
        "bookId": 24,
        "count": 1
    }, 
        {
        "userId": 1,
        "name": "Person 1",
        "status": "active",
        "bookId": 25,
        "count": 2
    }, 
    {
        "userId": 2,
        "name": "Person 2",
        "status": "active",
        "bookId": 8,
        "count": 2
    }, {
        "userId": 1,
        "name": "Person 1",
        "status": "active",
        "bookId": 24,
        "count": 3
    }, {
        "userId": 3,
        "name": "Person 3",
        "status": "active",
        "bookId": 99,
        "count": 1
    }
]

console.log(orders)

I am expecting an outcome that looks like:

const results = [
  {
    userId: 1,
    name: 'Person 1',
    status: 'active',
    orders: [
      {
        bookId: 24,
        count: 4,
      },
      {
        bookId: 25,
        count: 2,
      },
    ],
  },
  {
    userId: 2,
    name: 'Person 2',
    status: 'active',
    orders: [
      {
        bookId: 25,
        count: 2,
      },
    ],
  },

  {
    userId: 3,
    name: 'Person 3',
    status: 'active',
    orders: [
      {
        bookId: 99,
        count: 1,
      },
    ],
  },
];

console.log(results);
j08691
  • 204,283
  • 31
  • 260
  • 272
eneooo
  • 368
  • 3
  • 11
  • Does this answer your question? [How to group array of objects by certain property values](https://stackoverflow.com/questions/71505507/how-to-group-array-of-objects-by-certain-property-values) – pilchard Aug 16 '22 at 20:30
  • also: [In an array of objects how to group objects which have same value and include the values that differ](https://stackoverflow.com/questions/70955663/in-an-array-of-objects-how-to-group-objects-which-have-same-value-and-include-th) or [Group array items using object](https://stackoverflow.com/questions/31688459/group-array-items-using-object) – pilchard Aug 16 '22 at 20:31

3 Answers3

1

You can separate the orders by user Id into a map. Then you can check to see if you have already added an order that matches that book id, then aggregate the count of the books.

Then you can return the values from the map.

const aggregateOrders = orders => {
  // Separate orders in a map by user id to combine them
  const resultMap = {};
  for (let i = 0; i < orders.length; i++) {
    const order = orders[i];
    // If the user id of the order does not exist in the map, make an entry
    if (!resultMap[order.userId]) {
      resultMap[order.userId] = {
        userId: order.userId,
        name: order.name,
        status: order.status,
        orders: [],
      };
    }
    // Find the order with the matching book ID, if it exists increment count, or add a new entry
    const existingOrderWithMatchingBookId = resultMap[order.userId].orders.find(o => o.bookId === order.bookId);
    if (existingOrderWithMatchingBookId) {
      existingOrderWithMatchingBookId.count += order.count;
    } else {
      resultMap[order.userId].orders.push({
        bookId: order.bookId,
        count: order.count,
      });
    }
  }
  return Object.values(resultMap);
};
Uzair Ashraf
  • 1,171
  • 8
  • 20
1

As noted by the duplicate flags this is just a 'group-by' operation, though a nested one, first by userId and then within each user group by bookId.

The below snippet groups into an object by userId within a for...of loop and initializes another group object orders for each user group. After aggregating all the book data the result is the Object.values of the outer grouped object which is then mapped over to convert each orders object to an array of values as well.

const orders = [{ "userId": 1, "name": "Person 1", "status": "active", "bookId": 24, "count": 1 }, { "userId": 1, "name": "Person 1", "status": "active", "bookId": 25, "count": 2 }, { "userId": 2, "name": "Person 2", "status": "active", "bookId": 8, "count": 2 }, { "userId": 1, "name": "Person 1", "status": "active", "bookId": 24, "count": 3 }, { "userId": 3, "name": "Person 3", "status": "active", "bookId": 99, "count": 1 }];

const grouped = {};

for (const { userId, name, status, bookId, count } of orders) {
  const userGroup = (grouped[userId] ??= { userId, name, status, orders: {} });
  const bookGroup = (userGroup.orders[bookId] ??= { bookId, count: 0 });
  bookGroup.count += count;
}

const result = Object.values(grouped)
  .map(({ orders, ...rest }) => ({ ...rest, orders: Object.values(orders) }));

console.log(result);
pilchard
  • 12,414
  • 5
  • 11
  • 23
0

I have prepared a small algo for you. Reduce method iterate on the array and makes sure to remove duplicates with hash map methodology and sums up count if the same book is bought by the same user more than once.

const result = orders.reduce((acc = {}, order) => {
  if (acc[order.userId]) {
    let existingItem = acc[order.userId];
    const { orders = [] } = existingItem || {};
    const indexOfSameBook = orders.findIndex(
      ({ bookId }) => order.bookId === bookId
    );
    if (indexOfSameBook > -1) {
      orders[indexOfSameBook] = {
        bookId: order.bookId,
        count: orders[indexOfSameBook].count + order.count
      };
    } else {
      orders.push({ bookId: order.bookId, count: order.count });
    }

    existingItem = {
      ...existingItem,
      orders
    };
    return { ...acc, [order.userId]: existingItem };
  } else {
    return {
      ...acc,
      [order.userId]: {
        userId: order.userId,
        name: order.name,
        status: order.status,
        orders: [{ bookId: order.bookId, count: order.count }]
      }
    };
  }
}, {});

Also attaching a sandbox so you can check and test: https://codesandbox.io/s/sparkling-hill-1olun5?file=/src/index.js:504-1375

Talha Fayyaz
  • 481
  • 2
  • 9