Group and count the nested json response - Angular - javascript

Im trying to bind value in Angular Material table, before that i need to process the GET response v
trying to achieve like below(just for understanding)
my faulty code
let filterByLocation = data.reduce((r, { group: location.country, ...object }) => {
var finalArry = r.find(o => o.location === location);
if (!finalArry) r.push(temp = { location, locationObj: [] });
finalArry.locationObj.push(object);
return r;
}, []);
console.log(filterByLocation);
thanks to #Nishant Dixit for his working snippet
const finalResponse = data.response.reduce((r, {
location: {
country: group
},
...object
}) => {
r[group] = r[group] || {
location: group,
locationObj: []
};
r[group].locationObj.push(object);
return r;
}, {});
console.log(finalResponse)
const data = {
"totalRec": 5,
"response": [
{
"employee": {
"uid": 1,
"empName": "Jade"
},
"location": {
"country": "UK",
"subLocation": "London"
},
"department": {
"sector": "IT"
}
},
{
"employee": {
"uid": 2,
"empName": "Mike"
},
"location": {
"country": "UK",
"subLocation": "Manchester"
},
"department": {
"sector": "IT"
}
},
{
"employee": {
"uid": 3,
"empName": "Liya"
},
"location": {
"country": "UK",
"subLocation": "Southampton"
},
"department": {
"sector": "HR"
}
},
{
"employee": {
"uid": 3,
"empName": "Brad"
},
"location": {
"country": "USA",
"subLocation": "Texas"
},
"department": {
"sector": "IT"
}
},
{
"employee": {
"uid": 3,
"empName": "Brad"
},
"location": {
"country": "USA",
"subLocation": "Texas"
},
"department": {
"sector": "NON-IT"
}
}
]
};
but the problem is i'm getting result like
UK : {
location : "UK"
....
}
in html, i don't want to explicitly mention UK with dot operation like below, instead row.location
<ng-container matColumnDef="facility">
<mat-header-cell *matHeaderCellDef mat-sort-header> Facility</mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.UK.location}} </mat-cell>
</ng-container>
could someone tell me how to convert this output like
location: {
country : 'UK'
}
or any random name like
obj: {
location: 'UK'
//rest of values for grouping
}
fiddle
Thanks to every one

You can do something like this:
const countryMap = {};
data.response.forEach(item => {
countryMap[item.location.country] = [ ...( countryMap[item.location.country] || [] ), item];
});
Now, this is how the countryMap will look like:
Now, further to map it to the format you want, you can do this:
const mappedData = Object.entries(countryMap).map(entry => ({
location: entry[0],
response: entry[1]
}));
This will produce mappedData in this format:

Related

How to fomat nested Json using Vanilla Js

I want to transform the Input JSON to Output Array of Objects, Use Input JSON array and use only vanilla Javascript, loop through JSON object. I tried foreach function but faced some issues. print output like given below
let Input ={
details:[
{
"id":"Country_name",
"values":[
"India",
"England",
"Germany"
]
},
{
"id":"Country_capital",
"values":[
"Delhi",
"London",
"Berlin"
]
}
],
metadata:[
{
"id":"Country_name",
"label":"Country"
},
{
"id":"Country_capital",
"label":"Capital"
}
]
}
let Output =[
{
"Country":"India",
"Capital":"Delhi"
},
{
"Country":"England",
"Capital":"London"
},
{
"Country":"Germany",
"Capital":"Berlin"
}
]
Object.keys(input).forEach(function(value, key) {
input[value].forEach(function(v, k) {
console.log(v.id)
})
})
You can try something like this
const input = {
details: [{
"id": "Country_name",
"values": [
"India",
"England",
"Germany"
]
},
{
"id": "Country_capital",
"values": [
"Delhi",
"London",
"Berlin"
]
}
],
metadata: [{
"id": "Country_name",
"label": "Country"
},
{
"id": "Country_capital",
"label": "Capital"
}
]
};
function transform(input) {
const ids = {};
for (const detail of input.details) {
ids[detail.id] = detail.values;
}
const meta = {};
for (const m of input.metadata) {
meta[m.id] = m.label;
}
const idsKeys = Object.keys(ids);
const out = [];
for (let i = 0; i < ids[idsKeys[0]].length; i++) {
const obj = {};
for (const key of idsKeys) {
obj[meta[key]] = ids[key][i];
}
out.push(obj);
}
return out;
}
console.log(transform(input));
I changed the input and the variable in the top
This may work for you or not
let input ={
details:[
{
"id":"Country_name",
"values":[
"India",
"England",
"Germany"
]
},
{
"id":"Country_capital",
"values":[
"Delhi",
"London",
"Berlin"
]
}
],
metadata:[
{
"id":"Country_name",
"label":"Country"
},
{
"id":"Country_capital",
"label":"Capital"
}
]
}
let Output =[
{
"Country":"India",
"Capital":"Delhi"
},
{
"Country":"England",
"Capital":"London"
},
{
"Country":"Germany",
"Capital":"Berlin"
}
]
Object.keys(input).forEach(function(value, key) {
input[value].forEach(function(v, k) {
console.log(v.id)
})
})
If you have anymore questions or problems I would be happy to help
Countries are in Input.details[0].values
Capitals are in Input.details[1].values
Just make an object containing country on one go through countries, then add capital to each one on second pass through capitals
let Input = {
details: [{
"id": "Country_name",
"values": [
"India",
"England",
"Germany"
]
},
{
"id": "Country_capital",
"values": [
"Delhi",
"London",
"Berlin"
]
}
],
metadata: [{
"id": "Country_name",
"label": "Country"
},
{
"id": "Country_capital",
"label": "Capital"
}
]
};
let Output = [{
"Country": "India",
"Capital": "Delhi"
},
{
"Country": "England",
"Capital": "London"
},
{
"Country": "Germany",
"Capital": "Berlin"
}
];
const newOutput = [];
const countries = Input.details[0].values;
countries.forEach(country => {
console.log(country);
newOutput.push({"Country": country});
});
const capitals = Input.details[1].values;
capitals.forEach((capital, i) => {
console.log(capital);
newOutput[i]["Capital"] = capital;
});
console.log(newOutput);
a shorthand method to do this is mapping the data arrays of input :
let input ={
details:[
{
"id":"Country_name",
"values":[
"India",
"England",
"Germany"
]
},
{
"id":"Country_capital",
"values":[
"Delhi",
"London",
"Berlin"
]
}
],
metadata:[
{
"id":"Country_name",
"label":"Country"
},
{
"id":"Country_capital",
"label":"Capital"
}
]
}
let Output =[
{
"Country":"India",
"Capital":"Delhi"
},
{
"Country":"England",
"Capital":"London"
},
{
"Country":"Germany",
"Capital":"Berlin"
}
]
let data = input.details
let countries = data[0].values
let capitals = data[1].values
const output = countries.map((el, index) => ({"country": el, "capital": capitals[index]}))
console.log(output)
However, in your code, you are referring to Input object as input , which are two different variables

Reduce flat array to 2-level nested json object array

I have the following flat array:
{ "State": "New York", "Name": "Jane", "Product": "Apple" },
{ "State": "New York", "Name": "Jill", "Product": "Banana"},
{ "State": "California", "Name": "Jill", "Product": "Apple" },
{ "State": "California", "Name": "Jill", "Product": "Banana"}
Is it possible to create a 2-level nested array (i.e., Name > nested State Array > nested Product Array)? It would look like as follows:
{
"Name": "Jill",
"States": [
{
"State": "California",
"Products": [
{
"Product": "Apple"
},
{
"Product": "Banana"
}
]
},
{
"State": "New York",
"Products": [
{
"Product": "Banana"
}
]
}
]
},
{
"Name": "Jane",
"States": [
{
"State": "New York",
"Products": [
{
"Product": "Apple"
}
]
}
]
}
I have been able to get one level nested (States). How would you nest the second level?
Here is a stackblitz: https://stackblitz.com/edit/angular-lu6zj2
this.grouped_data = this.data.reduce((data, item) => {
data[item.Name] = data[item.Name] || { Name: item.Name, States: []}
data[item.Name].States.push(item)
return data;
}, {})
let data = [
{ "State": "New York", "Name": "Jane", "Product": "Apple" },
{ "State": "New York", "Name": "Jill", "Product": "Banana"},
{ "State": "California", "Name": "Jill", "Product": "Apple" },
{ "State": "California", "Name": "Jill", "Product": "Banana"}
];
let grouped = data.reduce((p, n) => {
// Create the Lady
if (!p[n.Name]) p[n.Name] = { States: [] };
// Check if the state exists, if not create it, then push product into it
if (!p[n.Name].States.some(state => state.State === n.State)) {
p[n.Name].States.push({ State: n.State, Products: [n.Product] });
} else {
!p[n.Name].States.find(state => state.State === n.State).Products.push(n.Product);
}
return p;
}, {});
console.log(grouped);
After that you can also remove duplicated products if you want to. I'll let you deal with it !
EDIT I didn't respect your model, what a dumbass am I ... Here it is :
let data = [
{ "State": "New York", "Name": "Jane", "Product": "Apple" },
{ "State": "New York", "Name": "Jill", "Product": "Banana"},
{ "State": "California", "Name": "Jill", "Product": "Apple" },
{ "State": "California", "Name": "Jill", "Product": "Banana"}
];
let grouped = data.reduce((p, n) => {
// Create the Lady
if (!p.some(lady => lady.Name === n.Name)) p.push({ Name: n.Name, States: [] });
let lady = p.find(lady => lady.Name === n.Name);
// Check if the state exists, if not create it, then push product into it
if (!lady.States.some(state => state.State === n.State)) {
lady.States.push({ State: n.State, Products: [n.Product] });
} else {
lady.States.find(state => state.State === n.State).Products.push(n.Product);
}
return p;
}, []);
console.log(grouped);

Extracting values from array of objects

I have an array that looks like this one:
[
{
"users": [
{
"name": "John",
"location": "USA",
"age": "34",
},
{
"name": "John",
"location": "California",
"address": "Silk Road 123"
},
{
"name": "Jane",
"last-name": "Edmus"
"location": "USA"
}
]
},
]
I want to merge the objects whose name match. I found this helper function:
findByMatchingProperties = (set, properties) => {
return set.filter(function (entry) {
return Object.keys(properties).every(function (key) {
return console.log(entry[key] === properties[key]);
});
});
}
But it is not budging. Any ideas on how I could go about this? The expected outcome should be:
[ { "users": [ { "name": "John", "location": ["USA", "California"}, "age": "34", "address": "Silk Road 123" }, { "name": "Jane", "last-name": "Edmus" "location": "USA" } ] }, ]
You could reduce the users array and group them based on the name. Destructure each user and get the name and rest of the properties separately. Loop through the keys of rest and check if the key already exists in the nested value. If it exists, create an array of values. Else, just add the value:
const input = [{users:[{name:"John",location:"USA",age:"34"},{name:"John",location:"California",address:"Silk Road 123"},{name:"Jane","last-name":"Edmus",location:"USA"}]}];
const merged = input[0].users.reduce((acc, o) => {
const { name, ...rest } = o;
const group = acc[name];
// check if name already exists in the accumulator
if(group) {
Object.keys(rest).forEach(key => {
if(key in group)
group[key] = [].concat(group[key], o[key])
else
group[key] = o[key];
})
}
else
acc[name] = o;
return acc;
},{})
const users = Object.values(merged)
console.log([{ users }])
This is what the merged object looks like:
{
"John": {
"name": "John",
"location": ["USA", "California"],
"age": "34",
"address": "Silk Road 123"
},
"Jane": {
...
}
}
Use Object.values() to get the values of this object to an array
You can achive this by using Map object for optimization and then converting it back to array. Check out code below.
const users = [
{ "name": "John", "location": "USA", "age": "34" },
{ "name": "John", "location": "California", "address": "Silk Road 123" },
{ "name": "John", "location": "Foo", "bar": "baz" },
{ "name": "Jane", "last-name": "Edmus", "location": "USA" }
];
const mergeObjectsExceptProps = (exceptProps, o1, o2) =>
Object.entries(o2).reduce((acc, [ k, v ]) => {
if (exceptProps.includes(k)) {
return acc
}
let propValueToSet
if (acc.hasOwnProperty(k)) {
propValueToSet = [
...(Array.isArray(acc[k]) ? acc[k] : [ acc[k] ]),
v
]
} else {
propValueToSet = v
}
return {
...acc,
[k]: propValueToSet,
}
}, o1)
const usersMap = new Map()
for (const user of users) {
const foundUser = usersMap.get(user.name)
if (foundUser) {
usersMap.set(user.name, mergeObjectsExceptProps([ 'name' ], foundUser, user))
} else {
usersMap.set(user.name, user)
}
}
const result = [ ...usersMap.values() ]
console.log(result)

Nested .unionWith, with one unique merge per level

Original data looks like that:
let AddressesBook = [
{
"userName": "Jay12",
"doorNumber": "1512",
"cityID": 19,
"city": "London",
"countryID": 1,
"country": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "2003",
"cityID": 14,
"city": "York",
"countryID": 1,
"universe": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "435",
"cityID": 31,
"city": "Washington",
"countryID": 2,
"universe": "USA",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "1123",
"cityID": 18,
"city": "Oxford",
"countryID": 1,
"universe": "UK",
"houseType": "private"
}
];
i was mapping the data hierarchy by relevant unique ID using Lodash and
a suppurated dictionary:
function nestMaker(list, order) {
if (_.isEmpty(order)) return [];
let groups = _.groupBy(list, _.first(order));
return _.map(groups, (children, key) => {
let group = {};
group[_.first(order)] = key;
group.data = nestMaker(children, _.drop(order));
return _.isEmpty(group.data) ? _.omit(group, 'data') : group;
});
}
let hierarchical = nestMaker(AddressesBook, [
"countryID",
"cityID",
"houseType",
"doorNumber"]
);
it works fine, but i would like to have the name relevant to the id in each level of the object.
unfortunately you can't use _.groupBy on two keys. i was thinking about using _.unionWith separately from the first iteration but i couldn't find a way to use it recursively omitting the unnecessary data.
expected output:
let output =
[
{
"countryID": "1",
"country": "UK",
"data": [
{
"cityID": "14",
"city": "York",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "2003"
}
]
}
]
},
{
"cityID": "18",
"city": "Oxford",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "1123"
}
]
}
]
},
{
"cityID": "19",
"city": "London",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "1512"
}
]
}
]
}
]
},
{
"countryID": "2",
"country": "USA",
"data": [
{
"cityID": "31",
"city": "Washington",
"data": [
{
"houseType": "private",
"data": [
{
"doorNumber": "435"
}
]
}
]
}
]
}
];
You can get the 1st item in the group, and extract the name (country, city) from the item:
const AddressesBook = [{"userName":"Jay12","doorNumber":"1512","cityID":19,"city":"London","countryID":1,"country":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"2003","cityID":14,"city":"York","countryID":1,"country":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"435","cityID":31,"city":"Washington","countryID":2,"country":"USA","houseType":"private"},{"userName":"Jay12","doorNumber":"1123","cityID":18,"city":"Oxford","countryID":1,"country":"UK","houseType":"private"}];
const nestMaker = (list, order) => {
if (_.isEmpty(order)) return [];
const idKey = _.first(order);
const nameKey = idKey.replace('ID', '');
let groups = _.groupBy(list, idKey);
return _.map(groups, (children, key) => {
const group = {};
const child = _.first(children);
group[idKey] = key;
if(_.has(child, nameKey)) group[nameKey] = child[nameKey];
group.data = nestMaker(children, _.drop(order));
return _.isEmpty(group.data) ? _.omit(group, 'data') : group;
});
}
const hierarchical = nestMaker(AddressesBook, [
"countryID",
"cityID",
"houseType",
"doorNumber"
]);
console.log(hierarchical);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
If the id and the name keys are doesn't follow the same pattern, you can explicitly state them as entry in the order:
const AddressesBook = [{"userName":"Jay12","doorNumber":"1512","cityID":19,"city":"London","countryID":1,"universe":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"2003","cityID":14,"city":"York","countryID":1,"universe":"UK","houseType":"private"},{"userName":"Jay12","doorNumber":"435","cityID":31,"city":"Washington","countryID":2,"universe":"USA","houseType":"private"},{"userName":"Jay12","doorNumber":"1123","cityID":18,"city":"Oxford","countryID":1,"universe":"UK","houseType":"private"}];
const nestMaker = (list, order) => {
if (_.isEmpty(order)) return [];
const entry = _.first(order);
const [idKey, nameKey] = Array.isArray(entry) ? entry : [entry];
let groups = _.groupBy(list, idKey);
return _.map(groups, (children, key) => {
const group = {};
const child = _.first(children);
group[idKey] = key;
if(_.has(child, nameKey)) group[nameKey] = child[nameKey];
group.data = nestMaker(children, _.drop(order));
return _.isEmpty(group.data) ? _.omit(group, 'data') : group;
});
}
const hierarchical = nestMaker(AddressesBook, [
["countryID", "universe"],
["cityID", "city"],
"houseType",
"doorNumber"
]);
console.log(hierarchical);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
This is a bit manual but does the job.
let AddressesBook = [{
"userName": "Jay12",
"doorNumber": "1512",
"cityID": 19,
"city": "London",
"countryID": 1,
"country": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "2003",
"cityID": 14,
"city": "York",
"countryID": 1,
"country": "UK",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "435",
"cityID": 31,
"city": "Washington",
"countryID": 2,
"country": "USA",
"houseType": "private"
},
{
"userName": "Jay12",
"doorNumber": "1123",
"cityID": 18,
"city": "Oxford",
"countryID": 1,
"country": "UK",
"houseType": "private"
}
];
database = []
AddressesBook.forEach(a => {
doesExist = database.some(d => (d.countryID == a.countryID))
if (doesExist) {
let instance = database.filter(d => d.countryID == a.countryID)[0]
instance.data.push({
"cityID": a.cityID,
"city": a.city,
"data": [{
"houseType": a.houseType,
"data": [{
"doorNumber": a.doorNumber
}]
}]
})
} else {
database.push({
"countryID": a.countryID,
"country": a.country,
"data": [{
"cityID": a.cityID,
"city": a.city,
"data": [{
"houseType": a.houseType,
"data": [{
"doorNumber": a.doorNumber
}]
}]
}]
})
}
})
console.log(database)

Nested Array in JSON Objects Conversion

I'm struggling with converting the nested JSON array that I have.
{
"Id": "1234",
"Company": {
"element": [{
"Name": "htc",
"Contacts": {
"element": [{
"name": "john",
"phone": "1234"
}, {
"name": "peter",
"phone": "5678"
}]
},
"Address": {
"element": {
"country": "us",
"state": "cali"
}
}
}, {
"Name": "samsung",
"Contacts": {
"element": [{
"name": "luke",
"phone": "0011"
}, {
"name": "james",
"phone": "2233"
}]
},
"Address": {
"element": {
"country": "us",
"state": "texas"
}
}
}]
}
}
As you'll notice, there's this "element" in the arrays "Company", "Contacts" and "Address". But the output that I need to provide should not contain the "element" such as this code:
{
"Id": "1234",
"Company": [{
"Name": "htc",
"Contacts": [{
"name": "john",
"phone": "1234"
}, {
"name": "peter",
"phone": "5678"
}],
"Address": [{
"country": "us",
"state": "cali"
}]
}, {
"Name": "samsung",
"Contacts": [{
"name": "luke",
"phone": "0011"
}, {
"name": "james",
"phone": "2233"
}],
"Address": [{
"country": "us",
"state": "texas"
}]
}]
}
I have no clue how to do in JavaScript. Any ideas/tips are appreciate.
Thank you
You can try something like this:
var data={Id:"1234",Company:{element:[{Name:"htc",Contacts:{element:[{name:"john",phone:"1234"},{name:"peter",phone:"5678"}]},Address:{element:{country:"us",state:"cali"}}},{Name:"samsung",Contacts:{element:[{name:"luke",phone:"0011"},{name:"james",phone:"2233"}]},Address:{element:{country:"us",state:"texas"}}}]}};
var keysToClean = ["Address", "Contacts"]
// Copy object instead of reference
var result = Object.assign({}, data);
result.Company = result.Company.element;
result.Company.forEach(x => {
keysToClean.forEach(k => {
x[k] = Array.isArray(x[k]) ? x[k].element : [x[k].element]
})
})
console.log(result);
Note: I have use Object.create and Arrow functions. They are not supported by old browsers. You can refer to following link for alternative to deep copy an object:
What is the most efficient way to deep clone an object in JavaScript?
The solution using Array.prototype.forEach() function:
var companyData = { "Id": "1234", "Company": { "element": [{ "Name": "htc", "Contacts": { "element": [{ "name": "john", "phone": "1234" }, { "name": "peter", "phone": "5678" }] }, "Address": { "element": { "country": "us", "state": "cali" } } }, { "Name": "samsung", "Contacts": { "element": [{ "name": "luke", "phone": "0011" }, { "name": "james", "phone": "2233" }] }, "Address": { "element": { "country": "us", "state": "texas" } } }] }
};
companyData.Company = companyData.Company.element;
var omitElement = function(o){
if (!o['element']) return o;
return (Array.isArray(o.element))? o.element : [o.element];
}
companyData.Company.forEach(function (o) {
o.Contacts = omitElement(o.Contacts);
o.Address = omitElement(o.Address);
});
console.log(companyData);
Please see this Plunker This should help.. it will generate desired result you need but be aware this is just a way to do this, and only meant for information purpose. It's not production grade...
function ParseData(data)
{
var newObject={Id:0, Company:[]};
newObject["Id"]=data["Id"];
newObject["Company"]=CreateCompanyObject(data["Company"]["element"]);
console.log(JSON.stringify(newObject));
}
function CreateCompanyObject(data)
{
var companies=[];
for(var i=0;i<data.length;i++)
{
companies.push({
name:data[i].Name,
contacts:CreateContactObject(data[i].Contacts.element),
Address:CreateAddressObject(data[i].Address.element)});
};
return companies;
}
function CreateContactObject(data){
var contacts=[];
for(var i=0;i<data.length;i++)
contacts.push(data[i]);
return contacts;
}
function CreateAddressObject(data){
var address=[];
if(typeof(data)=="array"){
for(var i=0;i<data.length;i++)
address.push(data[i]);
}
else
address.push(data);
return address;
}
You could check for element and move the content a step ahead to its parent.
function deleteElement(object){
Object.keys(object).forEach(function (k) {
if (object[k] && typeof object[k] === 'object') {
if ('element' in object[k]) {
object[k] = Array.isArray(object[k].element) ?
object[k].element :
[object[k].element];
}
deleteElement(object[k]);
}
});
}
var data = { Id: "1234", Company: { element: [{ Name: "htc", Contacts: { element: [{ name: "john", phone: "1234" }, { name: "peter", phone: "5678" }] }, Address: { element: { country: "us", state: "cali" } } }, { Name: "samsung", Contacts: { element: [{ name: "luke", phone: "0011" }, { name: "james", phone: "2233" }] }, Address: { element: { country: "us", state: "texas" } } }] } };
deleteElement(data);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources