Mapping an array of objects containing an empty array as property ignores the whole object

Viewed 71

I have an array of objects -

let initialArr = [
  {
    "id": 1,
    "name": "product name",
    "product_details": [
      {
        "id": 1,
        "details": "some details"
      }
    ],
    "subscriptions": [
      {
        "id": 1,
        "subs": "7 days"
      },
      {
        "id": 2,
        "subs": "15 days"
      }
    ]
  },
  {
    "id": 2,
    "name": "product name 2",
    "product_details": [
      {
        "id": 2,
        "details": "some details 2"
      }
    ],
    "subscriptions": [
      {
        "id": 1,
        "subs": "7 days"
      },
      {
        "id": 2,
        "subs": "15 days"
      }
    ]
  },
  {
    "id": 3,
    "name": "product name 3",
    "product_details": [
      {
        "id": 3,
        "details": "some details 3"
      }
    ],
    "subscriptions": []
  }
]

This is what I want to achieve -

[
  {
    "id": 1,
    "name": "product name",
    "detailsId" : 1,
    "details": "some details"
    "subsId": 1,
    "subs": "7 days"
  },
  {
    "id": 1,
    "name": "product name",
    "detailsId" : 1,
    "details": "some details"
    "subsId": 2,
    "subs": "15 days"
  },
  {
    "id": 2,
    "name": "product name 2",
    "detailsId" : 2,
    "details": "some details 2"
    "subsId": 1,
    "subs": "7 days"
  },
  {
    "id": 2,
    "name": "product name 2",
    "detailsId" : 2,
    "details": "some details 2"
    "subsId": 2,
    "subs": "15 days"
  },
  {
    "id": 3,
    "name": "product name 3",
    "detailsId" : 3,
    "details": "some details 3"
  }
]

This is what I have done -

initialArr.map(e => {
  e.product_details.map(p =>{
   e.subscriptions.map(s => {
      newArr.push({
        id: e.id,
        name: e.name,
        detailsId: p.id,
        details: p.details,
        subsId: s.id,
        subs:s.subs        
      }); 
   }); 
  })
})

This works if subscriptions array is not empty. If for some product, subscriptions array is empty, then that product is not pushed into the array. In I am unable to figure out how to solve it.

3rd product is not pushed in the new array. This is what I get -

[
  {
    "id": 1,
    "name": "product name",
    "detailsId" : 1,
    "details": "some details"
    "subsId": 1,
    "subs": "7 days"
  },
  {
    "id": 1,
    "name": "product name",
    "detailsId" : 1,
    "details": "some details"
    "subsId": 2,
    "subs": "15 days"
  },
  {
    "id": 2,
    "name": "product name 2",
    "detailsId" : 2,
    "details": "some details 2"
    "subsId": 1,
    "subs": "7 days"
  },
  {
    "id": 2,
    "name": "product name 2",
    "detailsId" : 2,
    "details": "some details 2"
    "subsId": 2,
    "subs": "15 days"
  }
]

Note: Although the same product is repeated twice in the new array, but this is the requirement - products according to "subs" property of subscriptions array.

Supposing i had more arrays e.g. 'Customizations', 'Orders', etc. besides 'Subscriptions' and i want those arrays' data also to be pushed, is this the correct way to do for multiple arrays?

3 Answers

Assuming array product_details has allways 1 element with it's data this is a sollution.
Using Array#reduce to accumulate your new result-array. Creating foreach element a new temp-object with the data for all of it. If the subscriptions-array is empty push this temp-object to your accumulated result-array. Otherwise use Array#forEach to iterate over your subscriptions. For every subscription use Object.assign to make a copy of your temp-object. Add to this the subscription-data and push it to the result-array.

const initialArr = [{ id: 1, name: "product name", product_details: [{ id: 1, details: "some details" }], subscriptions: [{ id: 1, subs: "7 days" }, { id: 2, subs: "15 days" }] }, { id: 2, name: "product name 2", product_details: [{ id: 2, details: "some details 2" }], subscriptions: [{ id: 1, subs: "7 days" }, { id: 2, subs: "15 days" }] }, { id: 3, name: "product name 3", product_details: [{ id: 3, details: "some details 3" }], subscriptions: [] }];

let res = initialArr.reduce((acc, cur) => {
    let temp = {
        id: cur.id,
        name: cur.name,
        detailsId: cur.product_details[0].id,
        details: cur.product_details[0].details
    }
    if (!cur.subscriptions.length)
        acc.push(temp);
    else {
        cur.subscriptions.forEach(subs => {
            let tempSub = Object.assign({}, temp);
            tempSub.subsId = subs.id;
            tempSub.subs = subs.subs;
            acc.push(tempSub);
        })
    }
    return acc;
}, []);

console.log(res);

Here a version without reduce and instead forEach:

const initialArr = [{ id: 1, name: "product name", product_details: [{ id: 1, details: "some details" }], subscriptions: [{ id: 1, subs: "7 days" }, { id: 2, subs: "15 days" }] }, { id: 2, name: "product name 2", product_details: [{ id: 2, details: "some details 2" }], subscriptions: [{ id: 1, subs: "7 days" }, { id: 2, subs: "15 days" }] }, { id: 3, name: "product name 3", product_details: [{ id: 3, details: "some details 3" }], subscriptions: [] }];

let acc = [];
initialArr.forEach(cur => {
    let temp = {
        id: cur.id,
        name: cur.name,
        detailsId: cur.product_details[0].id,
        details: cur.product_details[0].details
    }
    if (!cur.subscriptions.length)
        acc.push(temp);
    else {
        cur.subscriptions.forEach(subs => {
            let tempSub = Object.assign({}, temp);
            tempSub.subsId = subs.id;
            tempSub.subs = subs.subs;
            acc.push(tempSub);
        })
    }
});

console.log(acc);

You could check the length of subscription and return an object instead of mapping the array.

const
    data = [{ id: 1, name: "product name", product_details: [{ id: 1, details: "some details" }], subscriptions: [{ id: 1, subs: "7 days" }, { id: 2, subs: "15 days" }] }, { id: 2, name: "product name 2", product_details: [{ id: 2, details: "some details 2" }], subscriptions: [{ id: 1, subs: "7 days" }, { id: 2, subs: "15 days" }] }, { id: 3, name: "product name 3", product_details: [{ id: 3, details: "some details 3" }], subscriptions: [] }],
    result = data.flatMap(({ product_details: [{ id: detailsId, details }], subscriptions, ...o }) => subscriptions.length
        ? subscriptions.map(({ id: subsId, subs }) => ({ ...o, detailsId, details, subsId, subs }))
        : ({ ...o, detailsId, details })
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

you need to deal with the case explicitly:

if (e.subscriptions.length === 0) {
  newArr.push({ ... }
} else {
  e.subscriptions.map(...)
}

also you should use forEach instead of map.

Related