Reputation: 1931
Dataset:
var labels = ["Caterer", "INDEED", "Sarova Website", "Send to Friend", "alexandre-creed.com", "amdas.co.uk", "catabaseuk.co.uk", "cerecruit.co.uk", "el8recruitment.com", "equinoxresourcing.co.uk", "gatewayjobs.co.uk", "hotelstaffpec.co.uk", "jwrecruitment.co.uk", "marshallhr.co.uk", "marshallhr.com (Sarova)", "momentumrecruitment.com", "peoplebank.com", "ph-recruitment.co.uk", "platinum-hospitality.co.uk", "q", "talenthive.co.uk", "towngate-personnel.co.uk"];
var dataSet = {
"ADVERTS_PUBLISHED": ["102", "130", "153", "2", "2", "2", "2", "4", "2", "5", "2", "4", "3", "8", "4", "4", "4", "1", "3", "4", "1", "2"],
"VIEWS": ["1642", "10566", "45269", "7", "4", "1", "11", "9", "11", "12", "5", "0", "14", "6", "3", "13", "19", "2", "8", "3", "2", "2"],
"CLICKS": ["1992", "3628", "4458", "4", "2", "2", "7", "3", "11", "5", "6", "9", "10", "15", "10", "4", "34", "10", "10", "3", "0", "8"],
"SUBMITTED": ["1101", "877", "1290", "2", "3", "2", "10", "14", "11", "5", "5", "7", "6", "14", "8", "6", "17", "7", "9", "4", "1", "6"],
"PENDING": ["115", "26", "93", "0", "1", "0", "7", "3", "6", "2", "0", "0", "4", "8", "0", "3", "0", "0", "1", "0", "1", "0"],
"FILTERED": ["546", "493", "764", "1", "2", "2", "9", "12", "10", "5", "4", "5", "5", "12", "2", "3", "4", "6", "4", "0", "1", "5"],
"SHORTLISTED": ["37", "23", "32", "1", "0", "0", "0", "1", "0", "2", "0", "1", "0", "5", "0", "0", "0", "0", "2", "0", "0", "1"],
"REGRETTED": ["103", "28", "52", "0", "1", "0", "1", "6", "5", "3", "0", "2", "1", "1", "0", "0", "1", "3", "1", "0", "0", "2"],
"INTERVIEWED": ["62", "45", "88", "0", "0", "0", "1", "2", "2", "2", "0", "3", "0", "3", "0", "2", "0", "4", "0", "0", "0", "1"],
"OFFERED": ["4", "10", "20", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0"],
"OFFERED_AND_DECLINED": ["0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
"REGRETTED_AND_COMM": ["587", "334", "533", "0", "0", "2", "1", "1", "2", "1", "0", "1", "2", "4", "0", "1", "0", "0", "0", "2", "0", "0"],
"ACTUAL_HIRED": ["4", "2", "8", "0", "0", "0", "0", "0", "0", "0", "0", "2", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0"]
};
The following code computes an array named slices, that holds objects with properties label and value for all slices to be included in the chart. All values below 1 percent are summed and held by the object labeled "Others".
How would i go about making the "Others" always the last element in the slices array and remove it altogether if the Others value is 0?
//create dataset that includes the others grouping
dataSet2 = [];
Object.keys(dataSet).forEach(function (item) {
dataSet2[item] = groupOthers(dataSet[item], 1);
});
//groups data points together
function groupOthers(dataSet, thresholdPercent = 10) {
const values = dataSet.map(v => Number(v));
const valueSum = values.reduce((a, b) => a + b, 0);
const slices = values.map((v, i) => ({ label: labels[i], value: v }))
.reduce((accumulator, currObj) => {
const percent = 100 * currObj.value / valueSum;
if (percent < thresholdPercent) {
const others = accumulator.find(o => o.label == 'Others');
if (!others) {
return accumulator.concat({ label: 'Others', value: currObj.value });
}
others.value += currObj.value;
} else {
accumulator.push(currObj);
}
return accumulator;
}, []);
return slices;
}
I have tried altering the code like so but I am by no means a JS expert:
//find Others object in array and move it to the last element if it exists
obj = slices.find(o => o.label === 'Others');
if(obj !== null){
//move others to last element of array
slices.push(slices.shift());
//get last element of array
var last_element = slices[slices.length - 1];
if(last_element.label == 'Others'){
//if value of others is 0 remove last element from array
if(last_element.value == 0){
slices.splice(-1,1)
}
}
}
return slices;
Output would be something like the following
0: {label: "whatever", value: 4}
1: {label: "...", value: 2}
2: {label: "....", value: 1}
3: {label: "....", value: 1}
4: {label: "....", value: 2}
5: {label: "....", value: 1}
6: {label: "....", value: 1}
7: {label: "....", value: 1}
8: {label: "....", value: 1}
9: {label: "Others", value: 2}
Upvotes: 0
Views: 64
Reputation: 28206
I modified your script just a tiny bit for it to return the result you want:
Before returning slices
the array is sorted using a special compare function:
function(a,b){
return (a.label=='Others'?1:0)-(b.label=='Others'?1:0);
}
This function will keep the order of all elements but the ones with label=="Others"
unchanged.
Here is the working snippet:
var labels = ["Caterer", "INDEED", "Sarova Website", "Send to Friend", "alexandre-creed.com", "amdas.co.uk", "catabaseuk.co.uk", "cerecruit.co.uk", "el8recruitment.com", "equinoxresourcing.co.uk", "gatewayjobs.co.uk", "hotelstaffpec.co.uk", "jwrecruitment.co.uk", "marshallhr.co.uk", "marshallhr.com (Sarova)", "momentumrecruitment.com", "peoplebank.com", "ph-recruitment.co.uk", "platinum-hospitality.co.uk", "q", "talenthive.co.uk", "towngate-personnel.co.uk"];
var dataSet = {
"ADVERTS_PUBLISHED": ["102", "130", "153", "2", "2", "2", "2", "4", "2", "5", "2", "4", "3", "8", "4", "4", "4", "1", "3", "4", "1", "2"],
"VIEWS": ["1642", "10566", "45269", "7", "4", "1", "11", "9", "11", "12", "5", "0", "14", "6", "3", "13", "19", "2", "8", "3", "2", "2"],
"CLICKS": ["1992", "3628", "4458", "4", "2", "2", "7", "3", "11", "5", "6", "9", "10", "15", "10", "4", "34", "10", "10", "3", "0", "8"],
"SUBMITTED": ["1101", "877", "1290", "2", "3", "2", "10", "14", "11", "5", "5", "7", "6", "14", "8", "6", "17", "7", "9", "4", "1", "6"],
"PENDING": ["115", "26", "93", "0", "1", "0", "7", "3", "6", "2", "0", "0", "4", "8", "0", "3", "0", "0", "1", "0", "1", "0"],
"FILTERED": ["546", "493", "764", "1", "2", "2", "9", "12", "10", "5", "4", "5", "5", "12", "2", "3", "4", "6", "4", "0", "1", "5"],
"SHORTLISTED": ["37", "23", "32", "1", "0", "0", "0", "1", "0", "2", "0", "1", "0", "5", "0", "0", "0", "0", "2", "0", "0", "1"],
"REGRETTED": ["103", "28", "52", "0", "1", "0", "1", "6", "5", "3", "0", "2", "1", "1", "0", "0", "1", "3", "1", "0", "0", "2"],
"INTERVIEWED": ["62", "45", "88", "0", "0", "0", "1", "2", "2", "2", "0", "3", "0", "3", "0", "2", "0", "4", "0", "0", "0", "1"],
"OFFERED": ["4", "10", "20", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0"],
"OFFERED_AND_DECLINED": ["0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
"REGRETTED_AND_COMM": ["587", "334", "533", "0", "0", "2", "1", "1", "2", "1", "0", "1", "2", "4", "0", "1", "0", "0", "0", "2", "0", "0"],
"ACTUAL_HIRED": ["4", "2", "8", "0", "0", "0", "0", "0", "0", "0", "0", "2", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0"]
};
//create dataset that includes the others grouping
var res = Object.keys(dataSet).reduce(function(ds,item) {
ds[item] = groupOthers(dataSet[item],1); return ds;
}, {} );
console.log(res);
//groups data points together
function groupOthers(dataSet, thresholdPercent = 10) {
const values = dataSet.map(v => Number(v));
const valueSum = values.reduce((a, b) => a + b, 0);
const slices = values.map((v, i) => ({ label: labels[i], value: v }))
.reduce((accumulator, currObj) => {
const percent = 100 * currObj.value / valueSum;
if (percent < thresholdPercent) {
const others = accumulator.find(o => o.label == 'Others');
if (!others) {
return accumulator.concat({ label: 'Others', value: currObj.value });
}
others.value += currObj.value;
} else {
accumulator.push(currObj);
}
return accumulator;
}, []);
// ====== ONLY CHANGE HAPPENS HERE ============= START ===
// slices are sorted, so "Others" will always come last:
return slices.sort(function(a,b){
return (a.label=='Others'?1:0)-(b.label=='Others'?1:0);
});
// ====== ONLY CHANGE HAPPENS HERE =============== END ===
}
<div id="output"></div>
I have to concede, that @trincot's approach is to be preferred. His code solves the whole issue in a more direct way and makes my "sorting business" superfluous. Still, my code can be seen as a "polyfill" to make your originally supplied code work.
Upvotes: 0
Reputation: 350771
You can use Object.entries
to switch first to array format, do the mapping and filtering, and use the opposite Object.fromEntries
to get back the object representation:
var labels = ["Caterer", "INDEED", "Sarova Website", "Send to Friend", "alexandre-creed.com", "amdas.co.uk", "catabaseuk.co.uk", "cerecruit.co.uk", "el8recruitment.com", "equinoxresourcing.co.uk", "gatewayjobs.co.uk", "hotelstaffpec.co.uk", "jwrecruitment.co.uk", "marshallhr.co.uk", "marshallhr.com (Sarova)", "momentumrecruitment.com", "peoplebank.com", "ph-recruitment.co.uk", "platinum-hospitality.co.uk", "q", "talenthive.co.uk", "towngate-personnel.co.uk"];
var dataSet = {"ADVERTS_PUBLISHED": ["102", "130", "153", "2", "2", "2", "2", "4", "2", "5", "2", "4", "3", "8", "4", "4", "4", "1", "3", "4", "1", "2"],"VIEWS": ["1642", "10566", "45269", "7", "4", "1", "11", "9", "11", "12", "5", "0", "14", "6", "3", "13", "19", "2", "8", "3", "2", "2"],"CLICKS": ["1992", "3628", "4458", "4", "2", "2", "7", "3", "11", "5", "6", "9", "10", "15", "10", "4", "34", "10", "10", "3", "0", "8"],"SUBMITTED": ["1101", "877", "1290", "2", "3", "2", "10", "14", "11", "5", "5", "7", "6", "14", "8", "6", "17", "7", "9", "4", "1", "6"],"PENDING": ["115", "26", "93", "0", "1", "0", "7", "3", "6", "2", "0", "0", "4", "8", "0", "3", "0", "0", "1", "0", "1", "0"],"FILTERED": ["546", "493", "764", "1", "2", "2", "9", "12", "10", "5", "4", "5", "5", "12", "2", "3", "4", "6", "4", "0", "1", "5"],"SHORTLISTED": ["37", "23", "32", "1", "0", "0", "0", "1", "0", "2", "0", "1", "0", "5", "0", "0", "0", "0", "2", "0", "0", "1"],"REGRETTED": ["103", "28", "52", "0", "1", "0", "1", "6", "5", "3", "0", "2", "1", "1", "0", "0", "1", "3", "1", "0", "0", "2"],"INTERVIEWED": ["62", "45", "88", "0", "0", "0", "1", "2", "2", "2", "0", "3", "0", "3", "0", "2", "0", "4", "0", "0", "0", "1"],"OFFERED": ["4", "10", "20", "0", "0", "0", "0", "0", "0", "0", "0", "3", "0", "2", "0", "0", "0", "0", "0", "0", "0", "0"],"OFFERED_AND_DECLINED": ["0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"],"REGRETTED_AND_COMM": ["587", "334", "533", "0", "0", "2", "1", "1", "2", "1", "0", "1", "2", "4", "0", "1", "0", "0", "0", "2", "0", "0"],"ACTUAL_HIRED": ["4", "2", "8", "0", "0", "0", "0", "0", "0", "0", "0", "2", "0", "1", "0", "0", "0", "0", "0", "0", "0", "0"]};
var threshold = 10;
var res = Object.fromEntries(Object.entries(dataSet).map(([metric, values]) =>
[metric, values
.filter(v => +v > threshold)
.map((v, i) => ({label: labels[i], value: +v}))
.sort((a, b) => b.value - a.value)
.concat({
label: "others",
value: values.reduce((acc, v) => acc + (+v <= threshold && +v), 0)
})
]
));
console.log(res);
Upvotes: 1