Home > Software design >  Sum and combine similar object elements in array if condition is met - Javascript
Sum and combine similar object elements in array if condition is met - Javascript

Time:03-30

I have an array of this shape:

[
  {
    unique: 8,
    views: 24,
    name: "https://l.instagram.com/",
    sec: 5.39,
  },
  {
    unique: 2,
    views: 20,
    name: "",
    sec: 0,
  },
  {
    unique: 2,
    views: 5,
    name: "https://www.tiktok.com/",
    sec: 5.39,
  },
  {
    unique: 4,
    views: 3,
    name: "https://l.instagram.com",
    sec: 2.00,
  },
  {
    unique: 1,
    views: 2,
    name: "https://www.tiktok.com",
    sec: 2.00,
  },
];

And I'm trying to combine the same referrers with sum of the values (views, unique etc) into a new array. The new array needs to be formed based on condition, basically referrer names are need to be formatted based on the type. E.g. all ("https://l.instagram.com/", "l.instagram.com", "https://www.instagram.com/" etc) referrers need to be called Instagram and same with others - YouTube, TikTok etc. and 'Direct' if it's null. This is what I have in mind as final results:

[
  {
    unique: 12,
    views: 27,
    name: "Instagram",
    sec: 7.39,
  },
  {
    unique: 2,
    views: 20,
    name: "Direct",
    sec: 0,
  },
  {
    unique: 3,
    views: 7,
    name: "TikTok",
    sec: 7.39,
  },
];

My thought is to map through the array and if the value includes the desired string it will sum the values in a new object. Maybe map or reduce, or a combination of both? Any guide or help is appreciated. Thanks in advance!

CodePudding user response:

(sorry, I missed this 'direct' requirement, will modify and append)

Array.map, and all array looping methods, iterate through the array an element at a time, without retaining information of previous elements, and blind to future elements so some means of keeping track of previous objects is needed while looping through your object.

In my proposed snippet solution, I declare an intermediate object containing objects for each of the different groups you want to collect together (tiktok and instagram in your case, - you may beed to extract these programatically if your initial data set is very complex but in this example I simply read them by eye).

Next, the original array of objects is looped through using a for-each loop. for each element (object), the .name property is examined and an attempt to extract a substring that can be used as a key to reference the matching object in the intermediate array. This was done by splitting the .name into an array, breaking the name string wherever a period (.) was found. Because your names are all of the type (something).tiktok.(something), the unique key (tiktok) can be extracted as the seceond element (index 1) of that split array:

// suppose current element.name is "https://www.tiktok.com";
key = element.name.split(".")[1];
// key now holds 'tiktok';

This key can now be used to reference the relevant object in the intermediate object of objects we declared earlier, and individual properties can be accessed and updated like this:

 intermediateObjectOfObjects[key].unique  = element.unique;

The statements of the for-each loop are put inside a conditional if block to ignore any where no name is defined.

When the for-each loop is finished, the data will now be in an object of objects (with the inner objects all updated as you want them).

So, finally we just need to move the updated objects into an array of their own:

// define a new array:
const finalObjectArray=[];
// iterate through the intermediate array, adding each inner object to the new array:

     for (const property in intermediateObjectOfObjects) {
       finalObjectArray.push(intermediateObjectOfObjects[property]);
     } // end for-in object iterations;

The final result is an array containing one object for each named group, with the values from the original array of objects combined for each.

const arrayOfObjects = [
  {
    unique: 8,
    views: 24,
    name: "https://l.instagram.com/",
    sec: 5.39,
  },
  {
    unique: 2,
    views: 20,
    name: "",
    sec: 0,
  },
  {
    unique: 2,
    views: 5,
    name: "https://www.tiktok.com/",
    sec: 5.39,
  },
  {
    unique: 4,
    views: 3,
    name: "https://l.instagram.com",
    sec: 2.00,
  },
  {
    unique: 1,
    views: 2,
    name: "https://www.tiktok.com",
    sec: 2.00,
  },
];

const intermediateObjectOfObjects = {

   tiktok: {
           unique: 0,
           views: 0,
           name: "https://www.tiktok.com",
           sec: 0
           },

instagram: {
           unique: 0,
           views: 0,
           name: "https://www.instagram.com",
           sec: 0
           },

direct: {
           unique: 0,
           views: 0,
           name: "direct",
           sec: 0
           }

} // end intermediate object;

const finalObjectArray=[];


  arrayOfObjects.forEach(element => {

     key = (element.name.split(".")[1]) ? element.name.split(".")[1] : 'direct' ;
     intermediateObjectOfObjects[key].unique  = element.unique;
     intermediateObjectOfObjects[key].views  = element.views;
     intermediateObjectOfObjects[key].sec  = element.sec;

});


     for (const property in intermediateObjectOfObjects) {
       finalObjectArray.push(intermediateObjectOfObjects[property]);
     } // end for-in object iterations;

console.log(finalObjectArray);

Edits to include 'direct' group for undefined object names:

Instead of the if conditional to test for a name, I instead used a ternary operator to ask "did split('.') return a valid string in element[1]? is so use it as name, if not use 'direct' as name"

(the syntax looks odd but means what I quotted above:

 key = (element.name.split(".")[1]) ? element.name.split(".")[1] : 'direct' ;

Of course, the intermediate object of objects also had to be amended to include 'direct' as a named object;

CodePudding user response:

You can use a combination of:

const data = [{unique: 8,views: 24,name: "https://l.instagram.com/",sec: 5.39}, {unique: 2,views: 20,name: "",sec: 0}, {unique: 2,views: 5,name: "https://www.tiktok.com/",sec: 5.39}, {unique: 4,views: 3,name: "https://l.instagram.com",sec: 2.00}, {unique: 1,views: 2,name: "https://www.tiktok.com",sec: 2.00}];

const result = Object.entries( 
    data
    .map(({name,...rest}) => ({...rest,name:name.split('.').slice(-2)[0] || "direct" }))
    .reduce((prev,{name,...rest}) =>
        ({...prev,[name]:Object.fromEntries(
            Object.entries(rest).map(([k,v]) => [k, (prev[name] && prev[name][k] || 0)   v])
        )}), {})
)
.map(([name,rest]) => ({name,...rest}));

console.log( result );

CodePudding user response:

Get the names in a separate array. Iterate that array and match it with the main array. You can also try getting the name of the website by splitting with (.) and in the is statement change === to includes. Something like this:

    let items = [
        {
          unique: 8,
          views: 24,
          name: "https://l.instagram.com",
          sec: 5.39,
        },
        {
          unique: 2,
          views: 20,
          name: "",
          sec: 0,
        },
        {
          unique: 2,
          views: 5,
          name: "https://www.tiktok.com",
          sec: 5.39,
        },
        {
          unique: 4,
          views: 3,
          name: "https://l.instagram.com",
          sec: 2.00,
        },
        {
          unique: 1,
          views: 2,
          name: "https://www.tiktok.com",
          sec: 2.00,
        },
    ];
  
  var resultArr = [];
  var valueArr = [...new Set(items.map(function(item){ return item.name }))];
  console.log(valueArr);
  valueArr.map((name) => {
      let crrItem = {
          name: name,
          unique: 0,
          views: 0,
          sec: 0
      }
      items.map((item) => {
        //   console.log(item.name === name);
          if(item.name === name){
              crrItem.unique  = item.unique;
              crrItem.views  = item.views;
              crrItem.sec  = item.sec;
            //   console.log(crrItem);
          }
      });
      if(crrItem.unique > 0){
          resultArr.push(crrItem);
      }
  });
  console.log(resultArr);

  • Related