reactjstypescriptarray-reduce

How to restructure data using reduce?


I am getting data from API structured like this:

interface ProductModel {
    prod_id: string,
    brand: string,
    ...
}

interface OrderModel {
    order_id: string,
    prod_id: string,
    ...
}

const data = {
    products: ProductModel[],
    orders: OrderModel[]
}

What I want is to restructure the data to group the orders of a product and the product info in one object:

const expectedStructure = {
    prod_id: string,
    brand: string,
    ...,
    orders: OrderModel[]
}

I suppose that with a reduce it could be done easily, but I don't quite understand how it works. Could someone help me with this example?


Solution

  • Reduce can be used but you dont really need it, simple .map with .filter and ... spread operator:

    const products = [
      { prod_id: "1", brand: "brand1" },
      { prod_id: "2", brand: "brand1" },
      { prod_id: "3", brand: "brand2" },
      { prod_id: "4", brand: "brand2" },
      { prod_id: "5", brand: "brand3" }
    ];
    const orders = [
      { order_id: "1", prod_id: "1" },
      { order_id: "2", prod_id: "2" },
      { order_id: "3", prod_id: "3" },
      { order_id: "4", prod_id: "3" },
      { order_id: "5", prod_id: "4" }
    ];
    
    const data = {
      products: products,
      orders: orders
    };
    
    function groupData(data) {
      if (!data) return data;
      return data.products.map((p) => ({
        ...p,
        orders: data.orders.filter((x) => x.prod_id === p.prod_id)
      }));
    }
    
    console.log(groupData(data));

    Adding a little optimization asked in comments using Map class. With that you will only need 1 loop over initial Orders array to build the Map object and then you will have a constant time of element retrieval:

    const products = [
      { prod_id: "1", brand: "brand1" },
      { prod_id: "2", brand: "brand1" },
      { prod_id: "3", brand: "brand2" },
      { prod_id: "4", brand: "brand2" },
      { prod_id: "5", brand: "brand3" }
    ];
    const orders = [
      { order_id: "1", prod_id: "1" },
      { order_id: "2", prod_id: "2" },
      { order_id: "3", prod_id: "3" },
      { order_id: "4", prod_id: "3" },
      { order_id: "5", prod_id: "4" }
    ];
    
    const data = {
      products: products,
      orders: orders
    };
    
    function buildMap(data) {
      const res = new Map();
      data.orders.forEach((order) => {
        if (res.has(order.prod_id)) {
          res.get(order.prod_id).push(order);
        } else {
          res.set(order.prod_id, [order]);
        }
      });
      return res;
    }
    
    function groupData(data) {
      if (!data) return data;
      const ordersMap = buildMap(data);
      return data.products.map((p) => ({
        ...p,
        orders: ordersMap.get(p.prod_id) || []
      }));
    }
    
    console.log(groupData(data));