Home > Enterprise >  loop throught array of objects then filter and also create new object within
loop throught array of objects then filter and also create new object within

Time:05-06

I have an array of objects like this below. As you can see, i have rows nested inside input and rows is also array of objects.

let input = [
    {
        "title": "ENGINEERING",
        "rows": [
            { "risk": "P1", "radarLink": "rdar://92642113" },
            { "risk": "P2", "radarLink": "rdar://92694095" },
            { "risk": "P3", "radarLink": "rdar://92694235" },
        ],
    },
    {
        "title": "ENVIRONMENT",
        "rows": [
            { "risk": "P1", "radarLink": "rdar://92684289" },
            { "risk": "P2", "radarLink": "rdar://92695531" },
            { "risk": "P5", "radarLink": "rdar://92424550" },
        ],
    },
]

First of all, i want to filter rows and only get those objects whose risk === 'P1'. Then i also want to create a new object named p2PlusRow which will hold the count of objects whose value of risk !== 'P1. This new object p2PlusRow will gave countas mentioned above. It also has a property named radarLink. This is nothing but concatenation of radarLink whose risk !== 'P1' For e.g. in ENGINEERING, there are 2 objects whose risk is not equal to P1. the radar links of those are rdar://92694095 and rdar://92694235. So the concatenated value would be rdar://92694095&92694235 (each radar ticket is appended as shown below

let output = [
    {
        "title": "ENGINEERING",
        "rows": [
            { "risk": "P1", "radarLink": "rdar://92642113" },
        ],
        "p2PlusRow": {
            "count": "2",
            "radarLink": "rdar://92694095&92694235"
        }
    },
    {
        "title": "ENVIRONMENT",
        "rows": [
            { "risk": "P1", "radarLink": "rdar://92684289" },
        ],
        "p2PlusRow": {
            "count": "2",
            "radarLink": "rdar://92695531&92424550"
        }
    },
]

In order to achieve this result, i tried a few things but i am stuck on getting the count and radarLink values dynamically. Below is the tried code

let output = input?.map((obj) => ({
   ...obj,
   rows: obj.rows.filter((row) => row.risk === 'P1',
   p2PlusRow: { count: 2, radarLink: 'link' } 
}));

As you can see p2PlusRow is static values as i have not figured out how to loop through rows and get count and radarLink values. can someone let me know how to proceed in this case.

CodePudding user response:

Here is a solution that will mutate the input array. If you are ok with that, you can use this

for (const title of input) {
    let count = 0;
    let rdars = [];
    title.rows = title.rows.filter((row) => {
        if (row.risk === 'P1') {
            return true;
        } else {
            count  ;
            rdars.push(row.radarLink.replace('rdar://', ''));
            return false;
        }
    });
    title['p2PlusRow'] = {};
    title.p2PlusRow['count'] = count;
    title.p2PlusRow['radarLink'] = 'rdar://'   rdars.join('&');
}

Working snippet in case you want to see it run:

let input = [
    {
        title: 'ENGINEERING',
        rows: [
            { risk: 'P1', radarLink: 'rdar://92642113' },
            { risk: 'P2', radarLink: 'rdar://92694095' },
            { risk: 'P3', radarLink: 'rdar://92694235' },
        ],
    },
    {
        title: 'ENVIRONMENT',
        rows: [
            { risk: 'P1', radarLink: 'rdar://92684289' },
            { risk: 'P2', radarLink: 'rdar://92695531' },
            { risk: 'P5', radarLink: 'rdar://92424550' },
        ],
    },
];

for (const title of input) {
    let count = 0;
    let rdars = [];
    title.rows = title.rows.filter((row) => {
        if (row.risk === 'P1') {
            return true;
        } else {
            count  ;
            rdars.push(row.radarLink.replace('rdar://', ''));
            return false;
        }
    });
    title['p2PlusRow'] = {};
    title.p2PlusRow['count'] = count;
    title.p2PlusRow['radarLink'] = 'rdar://'   rdars.join('&');
}

console.log(input);

CodePudding user response:

Solution

You can use map() and reduce() to do what you want.

let input = [{
    "title": "ENGINEERING",
    "rows": [{
        "risk": "P1",
        "radarLink": "rdar://92642113"
      },
      {
        "risk": "P2",
        "radarLink": "rdar://92694095"
      },
      {
        "risk": "P3",
        "radarLink": "rdar://92694235"
      },
    ],
  },
  {
    "title": "ENVIRONMENT",
    "rows": [{
        "risk": "P1",
        "radarLink": "rdar://92684289"
      },
      {
        "risk": "P2",
        "radarLink": "rdar://92695531"
      },
      {
        "risk": "P5",
        "radarLink": "rdar://92424550"
      },
    ],
  },
]

const output = input.map(department => {
  const rest = department.rows.reduce((acc, cur) => {
    if (cur.risk === "P1") acc.rows.push(cur);
    else {
      const id = cur.radarLink.split("//")[1];
      acc.p2PlusRow.count  ;
      acc.p2PlusRow.radarLink.push(id);
    }
    return acc;
  }, {
    rows: [],
    p2PlusRow: {
      count: 0,
      radarLink: []
    }
  });
  rest.p2PlusRow.radarLink = `rdar://${rest.p2PlusRow.radarLink.join("&")}`
  return {
    title: department.title,
    ...rest
  };
});

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

Explanation

This looks much more complicated than it actually is.

Primarily you want to map() as you want a result for each and every item in your array.

Then you need to reduce() the rows. What we do here is essentially creating a new object in the form you would like to have it. To do so we declare the following as a start value for our new object:

{
    rows: [],
    p2PlusRow: {
      count: 0,
      radarLink: [],
    }
}

Now using that start value, essentially you loop over all the rows and for each row you check if risk === "P1". If it is you just add it to the rows field. If it is not, we increase the counter and push the id within the radarLink onto an the radarLink array.

The result of the reduce() therefore has the filtered rows, the count value and an array of ids for the radar link. Now we only have to concatenate the resulting link using those ids and we can return a new object which holds the result of the reduce operation and the title as this remains untouched.

Remarks

Just two remarks on my solution:

  • You would not need to actually count as the array radarLink does the counting for you. So instead of counting within reduce() we could just set count to radarLink.length after the reduction which is arguably a minor improvement on the above. Because the difference is certainly (almost) irrelevant and I think it is easier to understand I won't update my solution.
  • To make this solution completely immutable you just need to create a copy of the risk obect when you push() it onto the rows array like this acc.rows.push({ ...cur }). Everything else already works on copies, therefore your input remains untouched.

CodePudding user response:

You could create a helper function, called partition, that behaves in a similar way to .filter(), however, it allows you to keep the elements you return false for. In the below implementation of that function, the elements you return false for are in the first index (0) of the returned array, and the elements that you return true for are in the second index (index 1) of the returned array partion() gives you. You can then use the elements array are not P1 to get the count by taking it's .length. You can also use this array to get the radarLink values by mapping each non P1 row to its radarLink without the leading rdar:// using .replace(), and then using .join('&') to concatenate all of them together:

const input = [ { "title": "ENGINEERING", "rows": [ { "risk": "P1", "radarLink": "rdar://92642113" }, { "risk": "P2", "radarLink": "rdar://92694095" }, { "risk": "P3", "radarLink": "rdar://92694235" }, ], }, { "title": "ENVIRONMENT", "rows": [ { "risk": "P1", "radarLink": "rdar://92684289" }, { "risk": "P2", "radarLink": "rdar://92695531" }, { "risk": "P5", "radarLink": "rdar://92424550" }, ], }, ];

const patition = (arr, pred) => {
  const res = [[], []];
  for(const elem of arr) {
    const idx =  pred(elem); // falsy = 0, truthy = 1
    res[idx].push(elem);
  }
  return res;
}

let output = input.map((obj) => {
  const [nonP1Rows, p1Rows] = patition(obj.rows, row => row.risk === "P1");
  const concatRadarLinks = nonP1Rows.map(row => row.radarLink.replace('rdar://', '')).join('&');
  return {
    title: obj.title,
    rows: p1Rows,
    p2PlusRow: {count: nonP1Rows.length, radarLink: `rdar://${concatRadarLinks}`}
  }
});

console.log(output);

  • Related