Handling Objects With Javascript Reduce - javascript

This is my object:
let data = [
{acnumber: 1, acname: "Mr X", acterm: "shorterm", acbalance: 150},
{acnumber: 2, acname: "Mr Y", acterm: "longterm", acbalance: 140},
{acnumber: 3, acname: "Mr Z", acterm: "shorterm", acbalance: 155}
]
Because I need information about number of accounts and account balance for 'shortterm' loans only, I can filter this data this way:
let shortTerm = data.filter((item)=>{
if (item.acterm === "shorterm") {
return item;
}
})
Then I can find number of shortterm loan accounts using this:
let numberOfAccounts = shortTerm.length;
Also, I can find sum of account balance using this:
let accountBalance = shortTerm.reduce((acc, item)=>{
return acc+item.acbalance;
},0)
I was wondering if there is any better or more precise ways of getting the same result. I will highly appreciate any assistance.

A single .reduce will work - add the property to the accumulator only if the object being iterated over is shortterm. Use concise return if you want.
let data = [
{acnumber: 1, acname: "Mr X", acterm: "shorterm", acbalance: 150},
{acnumber: 2, acname: "Mr Y", acterm: "longterm", acbalance: 140},
{acnumber: 3, acname: "Mr Z", acterm: "shorterm", acbalance: 155}
]
const balance = data.reduce(
(a, obj) => a + (obj.acterm === 'shorterm' ? obj.acbalance : 0),
0
);
console.log(balance);

Related

ES6 - Reorder an Array with respect to an Object

I have an array which is like :
const my_array = [
"S T"
"O P"
"Lend"
"GT"
"CT"
"AC"
];
and an object according to which this array is being reordered:
const AN_ORDER = {
'Lend': 1,
'C T': 2,
'S T': 3,
'AC': 4,
'O P': 5
};
for which there is already a solution done as
//keys and sortBy from Lodash
const new_array = keys(my_array);
return sortBy(new_array, ar => AN_ORDER[ar] || Number.MAX_SAFE_INTEGER);
this yields me new array as:
["Lend", "AC", "O P", "S T", "GT", "C T"]
I tried looking into documentation of this usage, but I do not understand the idea of second argument being used here in sortBy.
Now my need is to reorder the my_array in below sequence:
['Lend', 'C T', 'S T', 'GT', 'AC', 'O P']
Please help me understand the implementation and suggest a way I can reorder in the same logic.
Your key 'G T' is not in object 'AN_ORDER' so my output will look different than yours. but it's working right.
const myArray = [
'S T',
'O P',
'Lend',
'GT',
'C T',
'AC'
];
const AN_ORDER = {
'Lend': 1,
'C T': 2,
'S T': 3,
'AC': 4,
'O P': 5
};
const sortedCollection = _.sortBy(myArray, item => {
return AN_ORDER[item];
});
You have add array in second argument, here you can mention callback function or property name.
Here is the working solution
const my_array = [
"S T",
"O P",
"Lend",
"GT",
"C T",
"AC"
];
var AN_ORDER = {
"Lend": 1,
"C T": 2,
"S T": 3,
"AC": 4,
"O P": 5
};
console.log(_.sortBy(my_array, [a => {
return (AN_ORDER[a] || Number.MAX_SAFE_INTEGER);
}]));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Not sure what AN_ORDER represents, as there isn't enough information on what it represents exactly.
Uses the keys in AN_ORDER in the final array, because the specified output above uses that. Strips spaces before searching for matching keys, using a Set to search if they exist, and using an offset of 0.5 to insert non-existing keys into AN_ORDER for sorting.
const my_array = [
"S T",
"O P",
"Lend",
"GT",
"CT",
"AC"
];
const AN_ORDER = {
'Lend': 1,
'C T': 2,
'S T': 3,
'AC': 4,
'O P': 5
};
const keys = new Set(Object.keys(AN_ORDER).map(x=>x.replace(/\s/g,'')))
my_array.forEach( (x,i)=>!keys.has(x.replace(/\s/g,'')) && (AN_ORDER[x]=i+0.5) )
my_obj = _.sortBy(Object.entries(AN_ORDER), ([,x])=>x).map(([x,])=>x)
console.log(my_obj);
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>

How to sort just a specific part of an already sorted 2D Array depending on new values. But only if the first sorted values match in Javascript

I have the following problem:
I have an array with objects in it.
Every object has a score and a rank, like this:
[
{ "score": 20, "rank": 12 },
{ "score": 20, "rank": 7 },
{ "score": 34, "rank": 4 }
]
First of all, I sort this descending by the score and store it into a 2-dimensional array.
[34, 4]
[20, 12]
[20, 7]
But now, if there is the same score twice or more often I want those to be sorted by the rank. So whatever has the lowest rank will have a smaller index number. Resulting in:
[34, 4]
[20, 7]
[20, 12]
I really don't know how to do this, I made some approaches, but they are a way to bad to mention them.
You can check if the difference of score of two objects is 0 then return the difference of rank otherwise return difference of score
const arr = [
{ "score": 20, "rank": 12 },
{ "score": 20, "rank": 7 },
{ "score": 34, "rank": 4 }
]
let res = [...arr]
.sort((a,b) => (b.score - a.score) || (a.rank - b.rank))
.map(x => [x.score,x.rank]);
console.log(res)
Just use lodash and orderby 2 fields.
You could sort the array first and then just map over for the Object.values:
const arr = [
{ "score": 20, "rank": 12 },
{ "score": 20, "rank": 7 },
{ "score": 34, "rank": 4 }
]
let result = arr.sort((a,b) => (b.score - a.score) || (a.rank - b.rank))
.map(x => Object.values(x))
console.log(result)

Filtering object on keys

I am getting the data from SQL using GROUP CONCAT.
What I finally want to achieve is Output expected. I tried using filter and map but couldn't achieve the desired output.
How can we achieve this so as to get serviceObj contain the array of objects
Output expected
var a = [
{
"id": 1,
"membership_name": "basic",
"membership_price": "10",
"serviceObj" :[
{id :7, name:Multi-City Artisan Availability}, {id:3,name:Hair and/or Makeup},{id:6,Online Booking. Easy},{id:5, name:On Location. Whenever. Wherever},{id:4,name:2 Services / Month with Rollover}
],
} ..so on for 2 ids ]
INPUT
var a = [
{
"id": 1,
"membership_name": "basic",
"membership_price": "10",
"services_id": "7;3;6;5;4",
"services_names": "Multi-City Artisan Availability;Hair and/or Makeup;Online Booking. Easy;On Location. Whenever. Wherever;2 Services / Month with Rollover"
},
{
"id": 2,
"membership_name": "Elite",
"membership_price": "123",
"services_id": "10;9;12;8;11",
"services_names": "2 Services / Month with Rollover;Hair and/or Makeup;Online Booking. Easy;Personal Makeup Shopper (1 appt);On Location. Whenever. Wherever."
},
{
"id": 3,
"membership_name": "Exclusive",
"membership_price": "169",
"services_id": "14;17;13;20;16;19;15;18",
"services_names": "2 Services / Month with Rollover;Online Booking. Easy;Hair and/or Makeup;Choice of Updo / Downdo Hairstyle;On Location. Whenever. Wherever;Faux Lashes & Airbrush Included;Personal Makeup Shopper (1 appt);Multi-City Artisan Availability"
},
{
"id": 4,
"membership_name": "Life",
"membership_price": "7999.20",
"services_id": "21;30;25;29;24;27;23;26",
"services_names": "VALID FOR LIFE!;Personalized Customer Care;Online Booking. Easy.;Choice of Updo / Downdo Hairstyle;On Location. Whenever. Wherever.;Faux Lashes & Airbrush Included;Hair and/or Makeup **;Multi-City Artisan Availability"
}
];
var obj = {};
var k = [];
l = a.map(n=>{
var obj = {
id : n.id,
membership_name : n.membership_name,
membership_price : n.membership_price,
service : [
{services_id : n.services_id,services_names:n.services_names }
]
}
k.push(obj);
})
console.log(JSON.stringify(k));
If you split out the ids and names you can then map over them to create a serviceObj array that you can add to the returned object array:
const out = a.map(({ id, membership_name, membership_price, services_id, services_names }) => {
const ids = services_id.split(';');
const names = services_names.split(';');
const serviceObj = ids.map((id, i) => ({ id: Number(id), name: names[i]}));
return { id, membership_name, membership_price, serviceObj }
});
For every item in an a array, Array.prototype.split() its services_id and services_name fields on the ;. Assuming they both will have the same length, iterate both of them in parallel and create new array of objects, based on the fields from both of the arrays.
var a = [
{
"id": 1,
"membership_name": "basic",
"membership_price": "10",
"services_id": "7;3;6;5;4",
"services_names": "Multi-City Artisan Availability;Hair and/or Makeup;Online Booking. Easy;On Location. Whenever. Wherever;2 Services / Month with Rollover"
},
{
"id": 2,
"membership_name": "Elite",
"membership_price": "123",
"services_id": "10;9;12;8;11",
"services_names": "2 Services / Month with Rollover;Hair and/or Makeup;Online Booking. Easy;Personal Makeup Shopper (1 appt);On Location. Whenever. Wherever."
},
{
"id": 3,
"membership_name": "Exclusive",
"membership_price": "169",
"services_id": "14;17;13;20;16;19;15;18",
"services_names": "2 Services / Month with Rollover;Online Booking. Easy;Hair and/or Makeup;Choice of Updo / Downdo Hairstyle;On Location. Whenever. Wherever;Faux Lashes & Airbrush Included;Personal Makeup Shopper (1 appt);Multi-City Artisan Availability"
},
{
"id": 4,
"membership_name": "Life",
"membership_price": "7999.20",
"services_id": "21;30;25;29;24;27;23;26",
"services_names": "VALID FOR LIFE!;Personalized Customer Care;Online Booking. Easy.;Choice of Updo / Downdo Hairstyle;On Location. Whenever. Wherever.;Faux Lashes & Airbrush Included;Hair and/or Makeup **;Multi-City Artisan Availability"
}
];
var output = a.map((item) => {
var ids = item.services_id.split(';');
var names = item.services_names.split(';');
var objects = [];
ids.forEach((id, index) => {
objects.push({
id : parseInt(id),
name: names[index]
});
});
return {
"id": item.id,
"membership_name": item.membership_name,
"membership_price": item.membership_price,
"serviceObj": objects
}
});
console.log(output);

Easy way to sort 2 arrays in Javascript?

Sorry if this is a stupid question, but I'm learning JavaScript and was wondering if there was an easy way to sort 2 lists like the ones here:
var names=["item", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"];
var points=[12, 12345, 5765, 123, 3, 567765, 99, 87654, 881, 101];
How would I display the items from 'names' according to the corresponding value in 'points'? For example, for the above item6 would displayed first and item5 would be displayed last.
I don't know if it's easy enough, but you could make an array of objects, sort it by the value prop and just map to get only the name props.
let names = ["item", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"],
points = [12, 12345, 5765, 123, 3, 567765, 99, 87654, 881, 101],
res = names.map((v, i) => ({ name: v, val: points[i] }))
.sort((a, b) => b.val - a.val)
.map(v => v.name);
console.log(res);
Here's a somewhat lengthy solution (with a much more concise version below). The basic idea is to:
Sort the points array in descending order
Loop through the sorted array, and find each value's position in the original points array
Grab the corresponding item from the names array
Push the value into a new array
var names=["item", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"];
var points=[12, 12345, 5765, 123, 3, 567765, 99, 87654, 881, 101];
const sortedPoints = points.slice().sort(function(a, b) {
return b - a;
});
const sortedNames = [];
sortedPoints.forEach(function(val) {
const position = points.indexOf(val);
sortedNames.push(names[position]);
})
console.log(sortedNames)
For a more concise solution, following the same process above but taking advantage of some shortcuts:
const names = ["item", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"];
const points = [12, 12345, 5765, 123, 3, 567765, 99, 87654, 881, 101];
const sortedNames = points.slice().sort((a, b) => b - a).map(val => names[points.indexOf(val)]);
console.log(sortedNames)
Javascript doesn't have a zip function natively. But that is most of what you want to do here. A little utility library like underscore is pretty handy. You can view the annotated source if you just want to replicate a zip function yourself.
var zippedAndSorted = _.zip(names, points)
.sort(function(a, b) {
return b - a;
});
Then you can iterate over each pair:
zippedAndSorted.forEach(function(namePoint) {
console.log('Name: ' + namePoint[0] + ' Points: ' + namePoint[1]);
});

dc.js table on aggregated data

I've been trying to get my head around dc.js by duplicating the code from here, https://github.com/dc-js/dc.js/blob/develop/web/examples/table-on-aggregated-data.html, with my own data.
[
{"grade": 8, "category": "Math", "rating": 4},
{"grade": 8, "category": "English", "rating": 3},
{"grade": 8, "category": "Math", "rating": 1},
{"grade": 8, "category": "Math", "rating": 3},
{"grade": 8, "category": "Science", "rating": 1},
{"grade": 9, "category": "Science", "rating": 2},
{"grade": 9, "category": "Science", "rating": 5},
{"grade": 9, "category": "English", "rating": 5}
]
with the result being a table like this:
8 English 3
8 Math 2.67
8 Science 1
9 English 5
9 Science 3.5
I've put this together
var chart = dc.dataTable("#test");
d3.json("test.json", function(error, experiments) {
console.log(experiments);
var ndx = crossfilter(experiments),
exptDimension = ndx.dimension(function(d) {return +d.grade;}),
groupedDimension = exptDimension.group().reduce(
function (p, v) {
++p.number;
p.total += +v.rating;
p.category = v.category;
p.grade = v.grade;
p.avg = (p.total / p.number).toFixed(2);
console.log(p);
return p;
},
function (p, v) {
--p.number;
p.total -= +v.rating;
p.category = v.category;
p.grade = v.grade;
p.avg = (p.number == 0) ? 0 : Math.round(p.total / p.number).toFixed(2);
return p;
},
function () {
return {number: 0, total: 0, avg: 0, category:"", grade:"", }
}),
rank = function (p) { return "rank" };
chart
.width(768)
.height(480)
.dimension(groupedDimension)
.group(rank)
.columns([function (d) { console.log(d); return d.value.grade },
function (d) { return d.value.category },
function (d) { return d.value.avg }])
.sortBy(function (d) { return d.value.avg })
.order(d3.descending)
chart.render();
});
with this result:
9 English 4.00
8 Science 2.40
I feel like i'm missing something really simple. Does anybody have a suggestion on how to proceed?
Thanks
Yeah, it is simple - or mostly simple. There's an implicit requirement that you want to group by both grade and category, whereas you were grouping by grade only. Your reduce functions wouldn't make a lot of sense that way, because
p.category = v.category;
assumes there's only one category per grade. But if you ignore that, the answer was sort of "correct", in that the average rating for 9th graders is 4, and the average rating for 8th graders is 2.4.
To group by both grade and category, you can specify a dimension key with both values in it:
exptDimension = ndx.dimension(function(d) {return d.category+'/'+d.grade;}),
This constructs a string with both values in it. Some people just make an array key here, but I don't like that because it will do some implicit conversions to string, which is a bit slower.
So that was easy but it only orders by average rating:
9 English 5.00
9 Science 3.50
8 English 3.00
8 Math 2.67
8 Science 1.00
http://jsfiddle.net/gordonwoodhull/5sbdvtqv/2/
Another implicit requirement is that you want to order by grade in ascending order, and average rating in descending order. This is a bit tougher, because it means you need to specify a single key that encodes two opposite orders.
Here's one way to do it:
.sortBy(function (d) { return (20-d.value.grade)*1000 + d.value.avg; })
Yeah. Wow. That's going to be fun to maintain, but I don't know a better way to do it. It creates a sort key where grade is highly negative-weighted, and avg is weakly positive-weighted, which when combined with d3.descending, will do what you want.
End result:
8 English 3.00
8 Math 2.67
8 Science 1.00
9 English 5.00
9 Science 3.50
http://jsfiddle.net/gordonwoodhull/5sbdvtqv/9/

Categories