Join JSON with a common attribute - json

I have a JSON with product items with a code, name and quantity. Sometimes items are duplicated (same code and name, different quantity). I'd like to sum the quantity of them in a single one.
Ex:
FROM
{ items: [{ code: 1, name: 'A', quantity: 2.0 }, { code: 1, name: 'A', quantity: 3.0 }, { code: 2, name: 'B', quantity: 4.0 }] }
TO
{ items: [{ code: 1, name: 'A', quantity: 5.0 }, { code: 2, name: 'B', quantity: 4.0 }] }

This should work:
items_hash = { items: [{ code: 1, name: 'A', quantity: 2.0 }, { code: 1, name: 'A', quantity: 3.0 }, { code: 2, name: 'B', quantity: 4.0 }] }
items_hash[:items] = items_hash[:items].each_with_object([]) do |item, collected_items|
if item_in_collected_items = collected_items.find { |i| i[:code] == item[:code] }
item_in_collected_items[:quantity] += item[:quantity]
else
collected_items << item
end
end
p items_hash
#=> { items: [{ code: 1, name: 'A', quantity: 5.0 }, { code: 2, name: 'B', quantity: 4.0 }] }

Code
def group_em(h)
h.transform_values do |a|
a.each_with_object({}) do |g,h|
h.update([g[:code], g[:name]]=>g) do |_,o,n|
g.merge(quantity: o[:quantity] + n[:quantity])
end
end.values
end
end
Examples
h = { items: [{ code: 1, name: 'A', quantity: 2.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 2, name: 'B', quantity: 4.0 }] }
group_em(h)
#=> { items: [{ code: 1, name: 'A', quantity: 5.0 },
# { code: 2, name: 'B', quantity: 4.0 }] }
h = { items: [{ code: 1, name: 'A', quantity: 2.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 1, name: 'C', quantity: 6.0 },
{ code: 2, name: 'B', quantity: 4.0 }] }
group_em(h)
#=> {:items=>[{:code=>1, :name=>"A", :quantity=>5.0},
# {:code=>1, :name=>"C", :quantity=>6.0},
# {:code=>2, :name=>"B", :quantity=>4.0}]}
h = { items: [{ code: 1, name: 'A', quantity: 2.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 1, name: 'C', quantity: 6.0 },
{ code: 2, name: 'B', quantity: 4.0 }],
returns: [{ code: 2, name: 'B', quantity: 1.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 1, name: 'C', quantity: 1.0 },
{ code: 2, name: 'B', quantity: 2.0 }]
}
group_em(h)
#=> {:items=>[{:code=>1, :name=>"A", :quantity=>5.0},
# {:code=>1, :name=>"C", :quantity=>6.0},
# {:code=>2, :name=>"B", :quantity=>4.0}],
# :returns=>[{:code=>2, :name=>"B", :quantity=>3.0},
# {:code=>1, :name=>"A", :quantity=>3.0},
# {:code=>1, :name=>"C", :quantity=>1.0}]}
Explanation
See Hash#transform_values, Enumerable#each_with_object, Hash#merge and the form of Hash#update (aka merge!) that employs a block to determine the values of keys that are present in both hashes being merged. Here that block is:
do |_,o,n|
g.merge(quantity: o[:quantity] + n[:quantity])
end
The values of the three block variables, _, o and n, are defined in the doc. I've used an underscore (a valid local variable) as the first variable--the common key--to signify that it is not used in the block calculation. That is common practice.
Note that:
h.update([g[:code], g[:name]]=>g)
is shorthand for:
h.update({ [g[:code], g[:name]]=>g })

Related

How to left join two json array based on common id using RxJS

Need help on a problem with Rxjs join.
I have two observables with common id, I want to perform join to show records
from both objects based on id.
let emp = of([
{ id: 1, name: 'aa', age: 20 },
{ id: 2, name: 'bb', age: 21 },
{ id: 3, name: 'cc', age: 22 },
{ id: 4, name: 'dd', age: 23 }
]);
let details = of([
{ id: 1, address: 'ee' },
{ id: 1, address: 'ff' },
{ id: 2, address: 'gg' },
{ id: 2, address: 'hh' }
]);
I need the join like this:
[
{id: 1, name: 'aa', age: 20, details: [{address: 'ee'},{address: 'ff'}] },
{id: 2, name: 'bb', age: 21, details: [{address: 'gg'},{address: 'hh'}] },
{id: 3, name: 'cc', age: 22, details: undefined },
{id: 4, name: 'dd', age: 23, details: undefined },
]
I have tried like this:
forkJoin([emp, details]).pipe(
map(([_emp, _details]) =>
_emp.map((item) => {
return ( {
...item,
details: _details.find((item1) => {
return item1.id === item.id
})
})
})
)
).subscribe(console.log)
I got the result:
[
{id: 1, name: 'aa', age: 20, details: [{address: 'ee'}] }, // {address: 'ff'} missing
{id: 2, name: 'bb', age: 21, details: [{address: 'gg'}] }, // {address: 'hh'} missing
{id: 3, name: 'cc', age: 22, details: undefined },
{id: 4, name: 'dd', age: 23, details: undefined },
]
In this result second address is missing for id 1 and 2.
Instead of [{address: 'ee'},{address: 'ff'}], I got only first match [{address: 'ee'}] for id 1.
Instead of [{address: 'gg'},{address: 'hh'}], I got only first match [{address: 'gg'}] for id 2.
Please help me to accomplish the desired result
I think you should use filter() instead of find(). The find() operator returns only the first match.
forkJoin([emp, details]).pipe(
map(([_emp, _details]) =>
_emp.map((item) => {
return ({
...item,
// This is the line you have to modify:
details: _details.filter((item1) => item1.id === item.id)
})
})
)
).subscribe(console.log)

Google script; setValues from API

I have problems set the values of an API into google sheets. The data set which is provided looks like this:
const data = [
{
quantity: 10000,
unitprice: 30.37,
id: 168586
},
{
quantity: 2000,
unitprice: 27.85,
id: 168583
},
{
quantity: 20,
unitprice: 150000,
id: 135693
},
{
quantity: 2109,
unitprice: 25.01,
id: 168586
},
{
quantity: 8434,
unitprice: 34.72,
id: 168589
},
{
quantity: 1,
unitprice: 5,
id: 168583
},
{
quantity: 560,
unitprice: 180.23,
id: 158191
}
];
If I use the setValue function in google sheets it only paste the first block but ignores the other ones. If I use setValues I get the error:
"Exception: The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues."
Someone has an idea what I can do?
Your data is not a 2D array, you have to detail each object with their properties
Try
function decode() {
const data = [
{
quantity: 10000,
unitprice: 30.37,
id: 168586
},
{
quantity: 2000,
unitprice: 27.85,
id: 168583
},
{
quantity: 20,
unitprice: 150000,
id: 135693
},
{
quantity: 2109,
unitprice: 25.01,
id: 168586
},
{
quantity: 8434,
unitprice: 34.72,
id: 168589
},
{
quantity: 1,
unitprice: 5,
id: 168583
},
{
quantity: 560,
unitprice: 180.23,
id: 158191
}
];
let values = []
values.push(['quantity', 'unitprice', 'id'])
data.forEach(d => {
values.push([d.quantity, d.unitprice, d.id])
})
SpreadsheetApp.getActiveSheet().getRange(1, 1, values.length, values[0].length).setValues(values)
}

Angular - Display data from 2 JSON in HTML

I'm having trouble displaying data from 2 interconnected JSON in Angular.
First JSON:
member = [
{ id: 1, name: 'shinta'},
{ id: 2, name: 'rahma'},
{ id: 3, name: 'john'}
]
Second JSON:
nest = [
{id: 1, amount: 2000, userid: 1},
{id: 2, amount: 3000, userid: 2},
{id: 3, amount: 5000, userid: 2},
{id: 4, amount: 1500, userid: 1},
{id: 5, amount: 2200, userid: 1},
{id: 6, amount: 12500, userid: 1},
{id: 7, amount: 7000, userid: 2},
{id: 8, amount: 8300, userid: 3},
{id: 9, amount: 2800, userid: 3},
{id: 10, amount: 9500, userid: 3}
]
How to view/bind data in HTML?
<ul *ngFor="let item of memberData, let i = index">
<li>{{ item.name }} <span>*this to view sum of user amount*</span></li>
</ul>
Please guide me to solve this problem. Thank you.
Use .map() to create new array with:
Duplicate object element of members (via ...x)
New field: totalAmount with .reduce() to perform aggregate sum.
this.memberData = this.member.map((x) => ({
...x,
totalAmount: this.nest
.filter((y) => y.userid == x.id)
.reduce((total: number, current) => (total += current.amount), 0),
}));
<ul *ngFor="let item of memberData; let i = index">
<li>
{{ item.name }} <span>{{ item.totalAmount }}</span>
</li>
</ul>
Demo on StackBlitz

Quey Post and comments and User with MySql and knex [duplicate]

I have a heavy array like this:
[
{Id: 1, Name: 'Red', optionName: 'Color'},
{Id: 2, Name: 'Yellow', optionName: 'Color'},
{Id: 3, Name: 'Blue', optionName: 'Color'},
{Id: 4, Name: 'Green', optionName: 'Color'},
{Id: 7, Name: 'Black', optionName: 'Color'},
{Id: 8, Name: 'S', optionName: 'Size'},
{Id: 11, Name: 'M', optionName: 'Size'},
{Id: 12, Name: 'L', optionName: 'Size'},
{Id: 13, Name: 'XL', optionName: 'Size'},
{Id: 14, Name: 'XXL', optionName: 'Size'}
]
What I need to do is to group them by optionName and have two row in the main array like this:
[
{
Name: 'Color',
Data:[{Id: 1, Name: 'Red'},
{Id: 2, Name: 'Yellow'},
{Id: 3, Name: 'Blue'},
{Id: 4, Name: 'Green'},
{Id: 7, Name: 'Black'}]
}, {
Name: 'Size',
Data:[{Id: 8, Name: 'S'},
{Id: 11, Name: 'M'},
{Id: 12, Name: 'L'},
{Id: 13, Name: 'XL'},
{Id: 14, Name: 'XXL'}]
}
]
How to do it in javascript?
This is a snippet I wrote for these kind of situations. You can add this functionality to all of your arrays:
Object.defineProperty(Array.prototype, 'group', {
enumerable: false,
value: function (key) {
var map = {};
this.forEach(function (e) {
var k = key(e);
map[k] = map[k] || [];
map[k].push(e);
});
return Object.keys(map).map(function (k) {
return {key: k, data: map[k]};
});
}
});
You can use it like this. You can just pass a function which defines how you want to group your data.
var newArray = arr.group(function (item) {
return item.optionName;
});
Working Fiddle
If you need, you can replace {key: k, data: map[k]} with {Name: k, Data: map[k]}.
This is also more compact ES6 version of the code above:
Object.defineProperty(Array.prototype, 'group', {
enumerable: false,
value: function (key) {
let map = {};
this.map(e => ({k: key(e), d: e})).forEach(e => {
map[e.k] = map[e.k] || [];
map[e.k].push(e.d);
});
return Object.keys(map).map(k => ({key: k, data: map[k]}));
}
});
Use it like this:
let newArray = arr.group(item => item.optionName))
An ES6 solution using Map object:
function groupBy(arr, key) {
return arr.reduce(
(sum, item) => {
const groupByVal = item[key];
groupedItems = sum.get(groupByVal) || [];
groupedItems.push(item);
return sum.set(groupByVal, groupedItems);
},
new Map()
);
}
var Data = [
{ Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
document.getElementById("showArray").innerHTML =JSON.stringify([...groupBy(Data, 'optionName')], null, 4);
<pre id="showArray"></pre>
You can use reduce to get the resultset you need:
var result = list.reduce(function(memo, item) {
if (item.optionName === 'Color') {
memo[0].Data.push(
Id: item.Id,
Name: item.Name
});
}
if (item.optionName === 'Size') {
memo[1].Data.push({
Id: item.Id,
Name: item.Name
});
}
return memo;
}, [{ Name: 'Color', Data: [] }, { Name: 'Size', Data: [] }]);
variable list is your first list.
Hope this helps.
This is a snippet I wrote for kind of my situation in my application functionality of all arrays. This snippet code is use in node js application. All the above is is given solution but I was finding some problem in server side in node js.
This snippet is user full me....
var Data= [
{ Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
function groupBy(arr, key) {
var newArr = [],
types = {},
newItem, i, j, cur;
for (i = 0, j = arr.length; i < j; i++) {
cur = arr[i];
if (!(cur[key] in types)) {
types[cur[key]] = { type: cur[key], data: [] };
newArr.push(types[cur[key]]);
}
types[cur[key]].data.push(cur);
}
return newArr;
}
I use it like this. I just pass a function which defines how you want to group our data.
filterData= groupBy(Data,'optionName');
Result of this snippet of code output.....
[
{"type":"Color","data":[{"Id":1,"Name":"Red","optionName":"Color"},
{"Id":2,"Name":"Yellow","optionName":"Color"},
{"Id":3,"Name":"Blue","optionName":"Color"},
{"Id":4,"Name":"Green","optionName":"Color"},
{"Id":7,"Name":"Black","optionName":"Color"}]},
{"type":"Size","data":[{"Id":8,"Name":"S","optionName":"Size"},
{"Id":11,"Name":"M","optionName":"Size"},
{"Id":12,"Name":"L","optionName":"Size"},
{"Id":13,"Name":"XL","optionName":"Size"},
{"Id":14,"Name":"XXL","optionName":"Size"}]}
]
Show on fiddle
var originalList = [ { Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
var output = [{ Name: "Color", Data: [] },{ Name: "Size", Data: [] }] ;
originalList.map(function(entry){
if ( entry.optionName === "Color") output[0].Data.push({ Id: entry.Id, Name: entry.Name });
if ( entry.optionName === "Size") output[1].Data.push({ Id: entry.Id, Name: entry.Name });
});
'use strict'
let l = [ { Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
let color = [];
let size = [];
l.forEach(element => {
if (element['optionName'] === 'Color') {
color.push({'Id': element.Id, 'Name': element.Name});
} else {
size.push({'Id': element.Id, 'Name': element.Name});
}
});
console.log(color);
console.log(size);
You can try this method.
All of the answers lead to the same result, so it all comes down to a personal preference (or company guidelines) on how to tackle this.
// ES5 (traditional javascript) version
function groupByOptionName(list, optionName) {
return list
// filter out any item whose optionName does not match the desired name
.filter(function(item) {
return item.optionName === optionName;
})
// map the item into the desired shape
// (appears to be everything except optionName itself
.map(function(item) {
return {
Id: item.Id,
Name: item.Name
};
})
}
// ES2015/ES6 version
function groupByOptionName(list, optionName) {
return list
// filter out any item whose optionName does not match the desired name
.filter(item => item.optionName === optionName)
// map the item into the desired shape
// (appears to be everything except optionName itself
.map(item => {
Id: item.Id,
Name: item.Name
});
}
This function would let you program the desired result as follows:
var output = [
{Name: 'Color', Data: groupByOptionName(list, 'Color')},
{Name: 'Size', Data: groupByOptionName(list, 'Size')},
];
// the ES2015/ES6 version of this code would replace var with let
While the code itself differs, it is much like the other answers, with only a variation on the steps needed.
One could also opt to leave out any hardcoded option names (Color and Size) by extracting those aswel, this would allow for a more dynamic input, but could also introduce more processing that actually needed.
// ES5 (traditional javascript) version
function getOptionNames(list) {
return list
// map the array into a list of optionNames
.map(function(item) {
return item.optionName;
})
// remove duplicates
.filter(function(item, index, all) {
return all.indexOf(item) === index;
});
}
// ES2015/ES6 version (does exactly the same as the one above)
function getOptionNames(list) {
return list
// map the array into a list of optionNames
.map(item => item.optionName)
// remove duplicates
.filter((item, index, all) => all.indexOf(item) === index);
}
Which allows the result to be fully based on the input data:
// ES5 (traditional javascript) version
var output = getOptionNames(list)
// map the names into the desired structure
.map(function(buffer, name) {
return {
Name: name,
Data: groupByOptionName(list, name)
};
});
// ES2015/ES6 version (does exactly the same as the one above)
var output = getOptionNames(list)
// map the names into the desired structure
.map((buffer, name) => {
Name: name,
Data: groupByOptionName(list, name)
});
By writing all of the data-mangling steps in short consice steps you'd do yourself (especially your future self) a favor if this code ever needs to be adjusted.
If the data set really is heavy (in terms of a lot of data), you must also make sure to keep the number of copies you keep in memory limited. For example, if you never need the original dataset, make sure it can be garbage collected (by not having a variable containing it outside the scope where you receive the data)
Usage:
groupValues([
{ color: 'blue', value: 100 },
{ color: 'blue', value: 75 },
{ color: 'yellow', value: 50 },
{ color: 'yellow', value: 25 }
], 'color')
Result:
[
[{ color: 'blue', value: 100 }, { color: 'blue', value: 75 }],
[{ color: 'yellow', value: 50 }, { color: 'yellow', value: 25 }]
]
Function:
const groupValues = function(arr, key) {
const mapped = {}
arr.forEach(el => {
const actualKey = el[key]
if(!mapped.hasOwnProperty(actualKey)) mapped[actualKey] = []
mapped[actualKey].push(el)
})
return Object.keys(mapped).map(el => mapped[el])
}

How to add new row next to existing row having similar key in json

I have two variables having json data as below.
var json1=
[{ name: 'AAA', id: 100},
{ name: 'BBB', id: 100 },
{ name: 'CCC', id: 101},
{ name: 'DDD', id: 102} ]
var json2=
[ { name: 'EEE', id: 101}
]
I need get combination of both variables as below.
var jsonCombined=
[{ name: 'AAA', id: 100},
{ name: 'BBB', id: 100 },
{ name: 'CCC', id: 101},
{ name: 'EEE', id: 101},
{ name: 'DDD', id: 102} ]
I tried to concatenate ,but it added to the last position.
Any help willbe appreciated.
Method one
If you know that json1 is already sorted by id values then you could iterate through the objects in json2 and use the .splice method to insert them.
var json1=
[{ name: 'AAA', id: 100},
{ name: 'BBB', id: 100 },
{ name: 'CCC', id: 101},
{ name: 'DDD', id: 102} ]
var json2=
[ { name: 'EEE', id: 101}
]
var jsonCombined = json1.slice(); //make a copy of json1
for (let obj of json2) {
for (let i = 0; i < jsonCombined.length; i++) {
if (jsonCombined[i]['id'] == obj['id']) {
jsonCombined.splice(i + 1, 0, obj);
break;
}
}
}
console.log(jsonCombined);
Method two
On the other hand, if you do not know that json1 is sorted then you can just combine the two json arrays and sort the objects based on their ids.
var json1=
[{ name: 'AAA', id: 100},
{ name: 'BBB', id: 100 },
{ name: 'CCC', id: 101},
{ name: 'DDD', id: 102} ]
var json2=
[ { name: 'EEE', id: 101}
]
var jsonCombined = json1.concat(json2).sort((a,b) => a['id'] > b['id'] ? 1 : -1);
console.log(jsonCombined);
try this
var combinedjson = [...json1, ...json2].sort((a, b) => a.id > b.id ? 1 : -1);