sineverba
sineverba

Reputation: 5172

Simplify and write better a series of filter / map and reduce functions

I want to simplify and get last part missing.

So, I have an array of objects like:

const data = [{
            "company": "aaa",
            "qty": 1,
            "id": 1
        }, {
            "company": "bbb",
            "qty": 14,
            "id": 2
        }, {
            "company": "ccc",
            "qty": 2,
            "id": 3
        }, {
            "company": "ddd",
            "qty": 5,
            "id": 4
        },
        {
            "company": "eee",
            "qty": 55,
            "id": 5
        }
    ];

I need to add the percentage based on qty and overall create a new object with detail of only companies with percentage greater than "X" (let's say 10) and others merged in a "other companies", like a similar

{
  "idCompany": 2,
  "name": "bbb",
  "percentage": 18.181818181818183,
  "qty": 14
},{
  "idCompany": 5,
  "name": "eee",
  "percentage": 71.42857142857143,
  "qty": 55
},{
  "idCompany": null,
  "name": "others",
  "percentage": 10.391 // 100 - 71.42xxx - 18.18
  "qty": 8 // 1 + 2 + 5
},

So, my steps:

  1. Get the total:

const total = data.reduce((i, el) => i+el.qty, 0);

  1. Add the percentage to start object:
let rawData = data.map(el => {
  return {
    name: el.company,
    qty: el.qty,
    idCompany: el.id,
    percentage: (el.qty/total)*100,
  }
})
  1. Get only companies with percentage greater than X (e.g. 10)

let consolidateCompanies = rawData.filter(el => el.percentage > 10);

But...

Now, how can I get the OTHERS companies and add to the consolidateCompanies object? And with a more beautiful code?

const data = [{
            "company": "aaa",
            "qty": 1,
            "id": 1
        }, {
            "company": "bbb",
            "qty": 14,
            "id": 2
        }, {
            "company": "ccc",
            "qty": 2,
            "id": 3
        }, {
            "company": "ddd",
            "qty": 5,
            "id": 4
        },
        {
            "company": "eee",
            "qty": 55,
            "id": 5
        }
    ];

// Get the total
const total = data.reduce((i, el) => i+el.qty, 0);

// Add percentage to the objects
let rawData = data.map(el => {
  return {
    name: el.company,
    qty: el.qty,
    idCompany: el.id,
    percentage: (el.qty/total)*100,
  }
})

// Get only companies that have percentage greater than 10

let consolidateCompanies = rawData.filter(el => el.percentage > 10);

console.log(consolidateCompanies);

// But I'm missing 1, 3 and 4

Upvotes: 0

Views: 73

Answers (4)

crystalthinker
crystalthinker

Reputation: 1182

Use forEach instead of map and filter, and loop the Object with your required need as follows.

const minPerc =10;
    const data = [{
            "company": "aaa",
            "qty": 1,
            "id": 1
        }, {
            "company": "bbb",
            "qty": 14,
            "id": 2
        }, {
            "company": "ccc",
            "qty": 2,
            "id": 3
        }, {
            "company": "ddd",
            "qty": 5,
            "id": 4
        },
        {
            "company": "eee",
            "qty": 55,
            "id": 5
        }
    ];

// Get the total
const total = data.reduce((i, el) => i+el.qty, 0);

let consolidateCompanies = [];

let otherObj={"idCompany": null,
  "name": "others",
  "percentage": 0,
  "qty": 0}; 

data.forEach(ele=>{ 
let perc=(ele.qty/total)*100; 
if(perc>minPerc){
consolidateCompanies.push({...ele,...{percentage:perc}})
}else{
otherObj.qty +=ele.qty; otherObj.percentage+=perc
} }); 
consolidateCompanies.push(otherObj);
console.log(consolidateCompanies);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386868

You could seperate others and add only if necessary without grouping by usining another object.

const
    data = [{ company: "aaa", qty: 1, id: 1 }, { company: "bbb", qty: 14, id: 2 }, { company: "ccc", qty: 2, id: 3 }, { company: "ddd", qty: 5, id: 4 }, { company: "eee", qty: 55, id: 5 }],
    totalQty = data.reduce((t, { qty }) => t + qty, 0),
    others = { idCompany: null, name: "others", percentage: 0, qty: 0 },
    result = data.reduce((r, { company: name, qty, id: idCompany }) => {
        const percentage = qty * 100 / totalQty;
        if (percentage >= 10) {
            r.push({ idCompany, name, percentage, qty });
        } else {
            others.qty += qty;
            others.percentage = others.qty * 100 / totalQty;
        }        
        return r;
    }, []);

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

Upvotes: 1

Terry Lennox
Terry Lennox

Reputation: 30725

You could do this with a couple of Array.reduce() calls.

We'd group companies either by name or 'others', adding percentage and quantity for the others.

const data = [{ "company": "aaa", "qty": 1, "id": 1 }, { "company": "bbb", "qty": 14, "id": 2 }, { "company": "ccc", "qty": 2, "id": 3 }, { "company": "ddd", "qty": 5, "id": 4 }, { "company": "eee", "qty": 55, "id": 5 } ]; 

const total = data.reduce((i, el) => i+el.qty, 0);

const minPercentage = 10;

const consolidateCompanies = Object.values(data.reduce((acc, el) => {
  const percentage = (el.qty/total)*100;
  const name = (percentage >= minPercentage) ? el.company: 'others';
  const id = (percentage >= minPercentage) ? el.id: null;
  
  acc[name] = acc[name] || { name, percentage: 0, qty: 0, id };
  acc[name].percentage += percentage;
  acc[name].qty += el.qty;
  
  return acc;
}, {}))

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

Upvotes: 1

Mohit Sharma
Mohit Sharma

Reputation: 648

here is the refactor of your code

const data = [{
            "company": "aaa",
            "qty": 1,
            "id": 1
        }, {
            "company": "bbb",
            "qty": 14,
            "id": 2
        }, {
            "company": "ccc",
            "qty": 2,
            "id": 3
        }, {
            "company": "ddd",
            "qty": 5,
            "id": 4
        },
        {
            "company": "eee",
            "qty": 55,
            "id": 5
        }
    ];
    
const total = data.reduce((i, el) => i+el.qty, 0)
const startWith = [{
  "idCompany": null,
  "name": "others",
  "percentage": 0,
  "qty": 0
}]
let rawData = data.reduce((acc, value) => {
  // console.log('value', value)
  const withPercentage =  {
    name: value.company,
    qty: value.qty,
    idCompany: value.id,
    percentage: (value.qty/total)*100,
  }
  if (withPercentage.percentage > 10) {
    acc.push(withPercentage)
  } else {
    acc[0].qty += withPercentage.qty; 
    acc[0].percentage += withPercentage.percentage; 
  }
  return acc;
}, startWith).filter(f => f.percentage !== 0 && f.qty !== 0);
console.log(rawData)

Upvotes: 0

Related Questions