Creating a SUM of nested object values in JavaScript - javascript

I'm using the following code to query an API, which is working well to return nested values in JSON:
const obj = response.data.map(function(item) {
return [item.id, item.jobNumber];
});
Example JSON:
{
"data": [
{
"id": 100,
"jobNumber": 1,
"jobTasks": [
{
"id": 12,
"cost": {
"amountString": 100
},
{
"id": 13,
"cost": {
"amountString": 500
}
}
}
]
},
{
"id": 101,
"jobNumber": 2,
"jobTasks": [
{
"id": 14,
"cost": {
"amountString": 100
},
{
"id": 15,
"cost": {
"amountString": 200
}
}
}]
}]
}
I'm wanting to now loop through the nested Job Tasks, and SUM the item.jobTasks.cost.amountString for each job, So that the following could be returned:
JobNumber1: Task Costs: 600
JobNumber2: Task Costs: 300

You can use reduce method which accepts a callback method.
Also, use forEach method in order to iterate data items.
var json={
"data": [
{
"id": 100,
"jobNumber": 1,
"jobTasks": [
{
"id": 12,
"cost": {
"amountString": 100
}
},
{
"id": 13,
"cost": {
"amountString": 500
}
}
]
},
{
"id": 101,
"jobNumber": 2,
"jobTasks": [
{
"id": 14,
"cost": {
"amountString": 100
}
},
{
"id": 15,
"cost": {
"amountString": 200
}
}
]
}]
}
json.data.forEach(function(item){
var sum=item.jobTasks.reduce(function(sum,elem){
return sum+elem.cost.amountString;
},0);
console.log('jobNumber'+item.jobNumber+' '+sum);
});

You can do it using Array#map() to create a new array and Array#reduce() to sum the amountString
const apiJson = {"data":[{"id":100,"jobNumber":1,"jobTasks":[{"id":12,"cost":{"amountString":100}},{"id":13,"cost":{"amountString":500}}]},{"id":101,"jobNumber":2,"jobTasks":[{"id":14,"cost":{"amountString":100}},{"id":15,"cost":{"amountString":200}}]}]};
const output = apiJson.data.map(d=>({
jobNumber : d.jobNumber,
tasksCost : d.jobTasks.reduce((a,b)=>a.cost.amountString+b.cost.amountString)
}));
console.log(output);

first Update your json , "}" is missing from jobTasks of second object of array data :
"jobTasks": [ { "id": 14,
"cost": {
"amountString": 100
}
},
{
"id": 15,
"cost": {
"amountString": 200
}
}
]
Now To get Output:
i = 0,1
item.jobTasks[i]cost.amountString;

Here is a solution using object-scan. We use it for most of our data processing now. It does take a moment to wrap your head around though as it is versatile.
// const objectScan = require('object-scan');
const getCounts = (data) => objectScan(['data[*].jobTasks[*].cost.amountString'], {
filterFn: ({ parents, value, context }) => {
const jobNumber = parents[3].jobNumber;
context[jobNumber] = (context[jobNumber] || 0) + value;
}
})(data, {});
const apiJson = { data: [{ id: 100, jobNumber: 1, jobTasks: [{ id: 12, cost: { amountString: 100 } }, { id: 13, cost: { amountString: 500 } }] }, { id: 101, jobNumber: 2, jobTasks: [{ id: 14, cost: { amountString: 100 } }, { id: 15, cost: { amountString: 200 } }] }] };
console.log(getCounts(apiJson));
// => { '1': 600, '2': 300 }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

Related

How to combine my array and json result in one?

I want to combine two json results, but im struggling getting it done..
first one (galleryData):
[
{ "userId": 2, "profile": { "profileImage": "image" } },
{ "userId": 4, "profile": { "profileImage": "image" } },
]
second one (combinations):
{
data: [
{ round: 1, partner: 2 },
{ round: 2, partner: 4 }
]
}
the output im expecting:
{
data: [
{ round: 1, userId: 2, "profile": { "profileImage": "image" } },
{ round: 2, userId: 4, "profile": { "profileImage": "image" } }
]
}
Basically I need the profileImage from one of my result and map it to the correct user id
What I tried so far with no success:
let combinedResult = galleryData["userId"].map((item, i) => Object.assign({}, item, combinations[i]));
You can use map and on each callback use find to find the corresponding userId === partner
const galleryData = [
{ "userId": 2, "profile": { "profileImage": "image" } },
{ "userId": 4, "profile": { "profileImage": "image" } },
]
const combinations = {
data: [
{ round: 1, partner: 2 },
{ round: 2, partner: 4 }
]
}
let combinedResult = {
data: galleryData.map((item, i) => {
let combination = combinations.data.find(c => c.partner === item.userId);
return { ...item, round: combination.round }
})
};
console.log(combinedResult)
I think a little try using Array.forEach and then merge the Objects
a = [
{ "userId": 2, "profile": { "profileImage": "image" } },
{ "userId": 4, "profile": { "profileImage": "image" } },
]
b = {
data: [
{ round: 1, partner: 2 },
{ round: 2, partner: 4 }
]
}
// inside forEach you can write the logic to get elements from array 'a' as you can use `find` to check which user is needed
b.data.forEach((i,e) => { b.data[e] = {...i, ...a[e]} })
console.log(b)
Hope it will be helpful.
let galleryData = [
{ "userId": 2, "profile": { "profileImage": "image" } },
{ "userId": 4, "profile": { "profileImage": "image" } },
];
let galleryDataAsUserId = {};
galleryData.forEach(elem=>{
galleryDataAsUserId[elem.userId] = elem;
})
let combinations = {
data: [
{ round: 1, partner: 2 },
{ round: 2, partner: 4 }
]
};
let data = combinations.data;
data.map(elem=>{
let newElem = elem;
newElem.profile = galleryDataAsUserId[elem.partner].profile;
});
console.log(data)

ReactJS Convert json to [name: value:] pair

I have this json object.
[
{
"crime": "LARCENY-NON_VEHICLE",
"count": "23217"
},
{
"crime": "AUTO_THEFT",
"count": "13675"
},
{
"crime": "LARCENY-FROM_VEHICLE",
"count": "28627"
},
{
"crime": "BURGLARY-RESIDENCE",
"count": "16312"
}
]
I need to display this data in a pie chart so I need to convert it to this format.
[
{name: "LARCENY-NON_VEHICLE", value: 23217},
{name: "AUTO_THEFT", value: 13675},
{name: "LARCENY-FROM_VEHICLE", value: 28627},
{name: "BURGLARY-RESIDENCE", value: 16312}
]
This is how Im retrieving the data using axios.
You can just use map to return a new array with values from old array
const data = [{
"crime": "LARCENY-NON_VEHICLE",
"count": "23217"
},
{
"crime": "AUTO_THEFT",
"count": "13675"
},
{
"crime": "LARCENY-FROM_VEHICLE",
"count": "28627"
},
{
"crime": "BURGLARY-RESIDENCE",
"count": "16312"
}
];
const newData = data.map(item => {
return {
name: item.crime,
value: +item.count // + to convert string to number
}
});
console.log(newData)
You can simply format an array by calling map function
const arr = [
{
crime: "LARCENY-NON_VEHICLE",
count: "23217",
},
{
crime: "AUTO_THEFT",
count: "13675",
},
{
crime: "LARCENY-FROM_VEHICLE",
count: "28627",
},
{
crime: "BURGLARY-RESIDENCE",
count: "16312",
},
];
arr.map((e) => ({
name: e.crime,
count: Number.parseInt(e.count)
}))

How can I compare 2 nested objects, subtracting the differences & returning the equals?

I have the below 2 variables.
const val1 = {
students: {
grade: [
{
subject: "Math3",
name: "jack",
mark: 60,
attendance: 90,
abscent: 2,
},
],
class: [
{
className: "math3",
students: 50,
absenteeism: 10,
requirment: [
{
subject: "math1",
mark: 60,
pass: 51,
},
{
subject: "math2",
mark: 60,
pass: 51,
},
],
},
],
},
};
const val2 = {
students: {
grade: [
{
subject: "Math3",
name: "jack",
mark: 80
},
],
class: [
{
className: "math3",
students: 40,
absenteeism: 10,
requirment: [
{
subject: "math1",
mark: 75,
pass: 51,
},
{
subject: "math2",
mark: 90,
pass: 51,
},
],
},
],
},
};
I am trying to get the below result by returning the the key and value if they are the same, & if the key is a number, return the difference of that number & if the item doesn't exist, skip. Like so.
students: {
grade: [
{
subject: "Math3",
name: "jack",
diff: 20
},
],
class: [
{
className: "math3",
diff: 10,
requirment: [
{
subject: "math1",
diff: 15,
},
{
subject: "math2",
diff: 30,
},
],
},
],
},
};
Code so far. I get the values but not the way i want it. Also, is there a more cleaner way.
const changeTable = (table1, table2) => {
const result = {};
result["students"] = {};
let file1= table1.students
let file2=table2.students
for (let x in file1) {
if (file2[x]) {
result.students[x] =file1[x]
.map((y) => file2[x]
.map((z)=> Object.keys(y)
.map((key)=> {
if (y[key] === z[key]) {
return {[key]:y[key]}
}
if (y[key] !== z[key]) {
if (typeof y[key] === "number") {
const res= Number(z[key])-Number(y[key])
if (!isNaN(res)) {
return {[key]:res}
}
}
}
}
).filter(i =>i)))
}
}
return result
};
changeTable(val1, val2)
Any advice is much appreciated.
I tried to match your expected output as closely as possible. In general terms, this is not a task suitable for array methods like map because what you really need here is recursive traversal of objects. I added comments to the code to show what it's doing, in case something is not quite right.
const val1 = {
"students": {
"grade": [
{
"subject": "Math3",
"name": "jack",
"mark": 60,
"attendance": 90,
"abscent": 2
}
],
"class": [
{
"className": "math3",
"students": 50,
"absenteeism": 10,
"requirment": [
{
"subject": "math1",
"mark": 60,
"pass": 51
},
{
"subject": "math2",
"mark": 60,
"pass": 51
}
]
}
]
}
}
const val2 = {
"students": {
"grade": [
{
"subject": "Math3",
"name": "jack",
"mark": 80
}
],
"class": [
{
"className": "math3",
"students": 40,
"absenteeism": 10,
"requirment": [
{
"subject": "math1",
"mark": 75,
"pass": 51
},
{
"subject": "math2",
"mark": 90,
"pass": 51
}
]
}
]
}
}
function diffObject(obj1, obj2) {
let result = {}
for (let key in obj1) {
// ignore properties not present in both objects
if (!(key in obj2)) continue
// ignore not same type, such as a number and string
if (typeof obj1[key] !== typeof obj2[key]) continue
// when both are number, set the property to the difference of the two
if (typeof obj1[key] === 'number') {
// only use different numbers
if (obj1[key] !== obj2[key]) {
result[key] = obj2[key] - obj1[key]
// in case you really wanted the "diff" property
// result.diff = obj2[key] - obj1[key]
}
}
// recursively iterate over each member of array
else if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) {
result[key] = obj1[key].map((item, index) => diffObject(item, obj2[key][index]))
}
// recursively iterate objects
else if (typeof obj1[key] === 'object' && obj1[key]) {
result[key] = diffObject(obj1[key], obj2[key])
}
// copy equal data
else if (obj1[key] === obj2[key]) {
result[key] = obj1[key]
}
// anything else is ignored
}
return result
}
console.log(diffObject(val1, val2))
There are edge cases that this cannot solve, such as recursive structures or non-plain objects.
Also, in your desired output, you keep using diff property, but in some cases, the objects have two numeric properties, so it would be overriden, I kept the original property name instead.

Array Sort by time hh:mm:ss

I am trying to sort the time. but I am unable to sort by time (hh:mm:ss) format. so i have used moments js. my array sort by time not get sorted. how sort array by using maps
I have an array of objects:
let elements =[
{
"id": 1,
"date": "02:01:02"
},
{
"id": 2,
"date": "01:01:01"
},
{
"id": 3,
"date": "03:01:01"
},
{
"id": 4,
"date": "04:01:01"
}
];
let parsedDates = new Map(
elements.map(e =>[["id", "date"],[e.id, moment(e.date, 'hh:mm:ss')]])
);
elements.sort((a, b) => parsedDates.get(a) - parsedDates.get(b));
console.log(elements.map(e => ({ id: e.id, date: e.date })));
You can lexicographical sort the time using string.localeCompare().
let times = [ { "id": 1, "date": "02:01:02" }, { "id": 2, "date": "01:01:01" }, { "id": 3, "date": "03:01:01" }, { "id": 4, "date": "04:01:01" } ];
times.sort((a,b) => a.date.localeCompare(b.date));
console.log(times);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can try this
function convertDateObj(hhmmss){
let obj = new Date();//creates a Date Object using the clients current time
let [hours,minutes,seconds] = hhmmss.split(':');
obj.setHours(+hours); // set the hours, using implicit type coercion
obj.setMinutes(minutes); //you can pass Number or String, it doesn't really matter
obj.setSeconds(seconds);
return obj;
}
let elements =[
{
"id": 1,
"date": "02:01:02"
},
{
"id": 2,
"date": "01:01:01"
},
{
"id": 3,
"date": "03:01:01"
},
{
"id": 4,
"date": "04:01:01"
}
];
elements.sort((a, b) => convertDateObj(a.date) - convertDateObj(b.date)); // Ascending order
elements.sort((a, b) => convertDateObj(b.date) - convertDateObj(a.date)); // Descending order
The parsedDates map you've created is looking like:
Map {
[ 'id', 'date' ] => [ 1, <some Date object> ],
[ 'id', 'date' ] => [ 2, <some Date object> ],
[ 'id', 'date' ] => [ 3, <some Date object> ],
[ 'id', 'date' ] => [ 4, <some Date object> ]
}
And then you try to extract from it with elements like this:
parsedDates.get({ "id": 1, "date": "02:01:02" })
This should not work, because the key in a Map is and Array instance.
Even if you were using an array as a key:
parsedDates.get([ 1, "02:01:02" ])
this still wouldn't work, as this would be a different Object reference. I mean two arrays
a = [ 1, "02:01:02" ]
b = [ 1, "02:01:02" ]
are stored in different places and are different Objects, even though their values are identical.
So, you can modify your solution a bit:
let elements =[
{
"id": 1,
"date": "02:01:02"
},
{
"id": 2,
"date": "01:01:01"
},
{
"id": 3,
"date": "03:01:01"
},
{
"id": 4,
"date": "04:01:01"
}
];
let parsedDates = new Map(
elements.map(e => [e.date, e])
);
elements = elements.map(x => x.date).sort().map(x => parsedDates.get(x))
console.log(elements)
// [
// { id: 2, date: '01:01:01' },
// { id: 1, date: '02:01:02' },
// { id: 3, date: '03:01:01' },
// { id: 4, date: '04:01:01' }
// ]

Mutate or Make a new Array by replacing a value

I have two arrays (and the length can be in 1000s):
I want my array to replace status to the status of array 2. Here is the output example:
[{
value: 123,
status: 'demo',
type: '...'
},
{value: 2335,
status: 'demo2',
type: 'xxx'
}]
As we can see it needs to get the status from another array and replace it. What are the most possible efficient solutions for this? As this array can be very large. I don't know a good approach to solve this problem.
Length and sort order can be different, I need to replace array1's status by the array2's status,
By linking Array1's status and Array2's id
My actual Data
[
{
"id": "55",
"status": "2",
"type": "COD",
"value": "5038.2",
},
{
"id": "56",
"status": "2",
"type": "COD",
"value": "5398.2",
},
{
"id": "57",
"status": "2",
"type": "COD",
"value": "10798.2",
}
]
Array 2
[
{
"id": "1",
"status": "Awaiting Confirmation",
},
{
"id": "2",
"status": "Confirmed",
},
{
"id": "3",
"status": "Awaiting Shipment",
},
{
"id": "4",
"status": "Awaiting Pickup",
},
{
"id": "5",
"status": "Shipped",
},
{
"id": "6",
"status": "Delivered",
},
{
"id": "7",
"status": "Cancelled",
},
{
"id": "8",
"status": "Refund Requested",
},
{
"id": "9",
"status": "Refunded",
}
Things i have tried...I have used lodash and a for loop to achieve this
const output = [];
for (let i = 0; i < array1.length; i++) {
const statuscode = array1[i].status;
const result = _.find(array2, { id: statuscode });
output.push({
value: array1[i].value,
status: result.status,
type: array1[i].type
});
}
console.log(output);
For high performance, transform one of the arrays to a Map first. Map lookups are very efficient:
const input1 = [{
value: 123,
status: 1,
type: 'COD',
},
{
value: 2335,
status: 2,
type: 'COD',
},
{
value: 222,
status: 3,
type: 'COD',
}
];
const input2 = [{
id: 1,
status: 'demo'
},
{
id: 2,
status: 'demo2'
}, {
id: 3,
status: 'demo3'
}
];
const map2 = new Map(Object.values(input2).map(({ id, status }) => [id, status]));
const output = input1.map(({ status, ...rest }) => {
const otherStatus = map2.get(status);
return { ...rest, status: otherStatus };
});
console.log(output);
Code readability generally matters more than speed, but if you wanted, you could transform the .map transformation into a for loop as well:
const input1 = [{
value: 123,
status: 1
},
{
value: 2335,
status: 2
},
{
value: 222,
status: 3
}
];
const input2 = [{
id: 1,
status: 'demo'
},
{
id: 2,
status: 'demo2'
}, {
id: 3,
status: 'demo3'
}
];
const map1 = new Map(Object.values(input1).map(({ value, status }) => [status, value]));
const output = [];
for (let i = 0; i < input2.length; i++) {
const { id, status } = input2[i];
output.push({ value: map1.get(id), status });
}
console.log(output);
A simple for loop would do:
for (let i = 0; i < array1.length; i++) {
array1[i].status = array2[i].status;
}
This of course assumes that the length and the order of the two arrays is the same.
EDIT
Alternative solution using Array.prototype.find and taking into account different lengths and orders.
for (let i = 0; i < array1.length; i++) {
const buffer = array1[i];
buffer.status = array2.find(x => x.id === buffer.status).status;
}
Also, I would highly recommend giving priority to readability over premature optimisation

Categories

Resources