How to iterate over JSON files - javascript

I am getting JSON data from two links, below is the structure of the files:
Stocks:
[{
"id": "efcd1502-265b-4e39-9a62-d8fbd96d49bb",
"stock_name": "AcelRx Pharmaceuticals, Inc.",
"shareholders": [{
"userId": "5d43449f-17a8-4747-b89a-ec2dd54b83ee",
"number_of_shares": 378
}, {
"userId": "91bef464-b2ee-4c18-8d9d-0781d30a3bcb",
"number_of_shares": 358
}, {
"userId": "c4a0c9ff-b934-4eda-b9fb-bd26afa21561",
"number_of_shares": 351
}, {
"userId": "2a9fd876-334f-425d-9135-4211ca92a886",
"number_of_shares": 499
}]
}]
People:
[{
"id": "20035a09-3820-4f49-bb8f-d947cebee537",
"first_name": "Merell",
"last_name": "Pecht",
"email": "mpecht0#uol.com.br",
"ip_address": "159.113.166.2",
"ssn": "819-97-0464",
"date_of_birth": "12/12/1998",
"address": {
"home": {
"street_number": "862",
"street_name": "Starling",
"street_suffix": "Hill",
"city": "Riverside",
"state": "CA",
"zip": "92519"
},
"work": {
"street_number": "3",
"street_name": "Grayhawk",
"street_suffix": "Circle",
"city": "Tucson",
"state": "AZ",
"zip": "85710"
}
}
}]
Below is my code for replacing the userID present in Stocks' shareholders array with first name and last name from the people file wherever the ID matches.
require("util").inspect.defaultOptions.depth = null;
const axios = require('axios');
async function getPeople(){
const { data } = await axios.get('https://gist.githubusercontent.com/graffixnyc/a1196cbf008e85a8e808dc60d4db7261/raw/9fd0d1a4d7846b19e52ab3551339c5b0b37cac71/people.json')
return data // this will be the array of people objects
}
async function getStocks(){
const { data } = await axios.get('https://gist.githubusercontent.com/graffixnyc/8c363d85e61863ac044097c0d199dbcc/raw/7d79752a9342ac97e4953bce23db0388a39642bf/stocks.json')
return data // this will be the array of people objects
}
async function listShareholders(){
let a = await getPeople();
let b = await getStocks();
let arr = {}
for(j=0;j<b.length;j++){
for(k=0;k<b[j].shareholders.length;k++){
let shareholders = []
let res = {}
for(i=0;i<a.length;i++){
if(b[j].shareholders[k].userId === a[i].id){
res['first_name'] = a[i].first_name
res['last_name'] = a[i].last_name
res['number of shares'] = b[j].shareholders[k].number_of_shares
shareholders.push(res)
arr['id'] = b[j].id
arr['stock_name'] = b[j].stock_name
arr['shareholders'] = shareholders
}
}
}
}
return arr
}
async function bc(){
const address = await listShareholders()
console.log(address)
}
bc()
The output is just one stock with the userID replaced with the name which is the last object in the stocks file. Below is the output I get. I actually need to get all of the stocks with the ID replaced. Not sure what I am doing wrong
{
id: 'efcd1502-265b-4e39-9a62-d8fbd96d49bb',
stock_name: 'AcelRx Pharmaceuticals, Inc.',
shareholders: [
{
first_name: 'Jenni',
last_name: 'Garrish',
'number of shares': 499
}
]
}
Code :
require("util").inspect.defaultOptions.depth = null;
const axios = require('axios');
async function getPeople(){
const { data } = await axios.get('https://gist.githubusercontent.com/graffixnyc/a1196cbf008e85a8e808dc60d4db7261/raw/9fd0d1a4d7846b19e52ab3551339c5b0b37cac71/people.json')
return data // this will be the array of people objects
}
async function getStocks(){
const { data } = await axios.get('https://gist.githubusercontent.com/graffixnyc/8c363d85e61863ac044097c0d199dbcc/raw/7d79752a9342ac97e4953bce23db0388a39642bf/stocks.json')
return data // this will be the array of people objects
}
async function listShareholders(){
let a = await getPeople();
let b = await getStocks();
b.forEach(stockItem => {
stockItem.shareholders = stockItem.shareholders.map(shareHolderItem => {
const person = a.find(e => e.id == shareHolderItem.userId);
return { first_name: person?.first_name,last_name: person?.last_name,number_of_shares: shareHolderItem.number_of_shares}
});
//Keep this if you just want to show mapped shareholder only
stockItem.shareholders = stockItem.shareholders.filter(e => e.first_name !== undefined);
return b
});
}
async function bc(){
const address = await listShareholders()
console.log(address)
}
bc()

You should use Array.map in case you want to change/map to a new structure.
const stocks = [{
"id": "efcd1502-265b-4e39-9a62-d8fbd96d49bb",
"stock_name": "AcelRx Pharmaceuticals, Inc.",
"shareholders": [{
"userId": "5d43449f-17a8-4747-b89a-ec2dd54b83ee",
"number_of_shares": 378
}, {
"userId": "91bef464-b2ee-4c18-8d9d-0781d30a3bcb",
"number_of_shares": 358
}, {
"userId": "c4a0c9ff-b934-4eda-b9fb-bd26afa21561",
"number_of_shares": 351
}, {
"userId": "2a9fd876-334f-425d-9135-4211ca92a886",
"number_of_shares": 499
}, { // my element to match to Merell Pecht
"userId": "20035a09-3820-4f49-bb8f-d947cebee537",
"number_of_shares": 9999
}]
}];
const people = [{
"id": "20035a09-3820-4f49-bb8f-d947cebee537",
"first_name": "Merell",
"last_name": "Pecht",
"email": "mpecht0#uol.com.br",
"ip_address": "159.113.166.2",
"ssn": "819-97-0464",
"date_of_birth": "12/12/1998",
"address": {
"home": {
"street_number": "862",
"street_name": "Starling",
"street_suffix": "Hill",
"city": "Riverside",
"state": "CA",
"zip": "92519"
},
"work": {
"street_number": "3",
"street_name": "Grayhawk",
"street_suffix": "Circle",
"city": "Tucson",
"state": "AZ",
"zip": "85710"
}
}
}];
stocks.forEach(stockItem => {
stockItem.shareholders = stockItem.shareholders.map(shareHolderItem => {
const person = people.find(e => e.id == shareHolderItem.userId);
return {
first_name: person?.first_name,
last_name: person?.last_name,
number_of_shares: shareHolderItem.number_of_shares
}
});
//Keep this if you just want to show mapped shareholder only
stockItem.shareholders = stockItem.shareholders.filter(e => e.first_name !== undefined);
});
console.log(stocks);
Update: Fix function
async function listShareholders(){
let a = await getPeople();
let b = await getStocks();
b.forEach(stockItem => {
stockItem.shareholders = stockItem.shareholders.map(shareHolderItem => {
const person = a.find(e => e.id == shareHolderItem.userId);
return { first_name: person?.first_name,last_name: person?.last_name,number_of_shares: shareHolderItem.number_of_shares}
});
//Keep this if you just want to show mapped shareholder only
stockItem.shareholders = stockItem.shareholders.filter(e => e.first_name !== undefined);
});
return b // <- This row
}

You can do this using Spread Operator and Array.map() functions:
NOTE: I've taken the liberty to simplify the objects for the sake of the example. This should work for the original object as well
const stocks = [{
"id": "efcd1502-265b-4e39-9a62-d8fbd96d49bb",
"stock_name": "AcelRx Pharmaceuticals, Inc.",
"shareholders": [{
"userId": "1",
"number_of_shares": 378
}, {
"userId": "2",
"number_of_shares": 358
}, {
"userId": "3",
"number_of_shares": 351
}, {
"userId": "4",
"number_of_shares": 499
}]
}]
const people = [{
"id": "1",
"first_name": "Tony",
"last_name": "Stark",
},
{
"id": "2",
"first_name": "Steve",
"last_name": "Rogers",
},
{
"id": "3",
"first_name": "Bruce",
"last_name": "Banner",
},
{
"id": "4",
"first_name": "Thor",
"last_name": "Odinson",
}]
const result = stocks.map(stock => {
return {
...stock,
shareholders: stock.shareholders.map(shareHolder => {
const {first_name, last_name} = people.find(person => person.id === shareHolder.userId);
return {
...shareHolder,
first_name,
last_name
}
})
}
})
console.log(JSON.stringify(result, null, 2));

Related

Refactor to use reduce for grouping array of objects by nested array text

I have a function that will accept 3 arguments; an array of data, a key and a partial value. I want to group the results by a value that may be in each objects nested array. It looks like my code is working, but I'm wondering if refactoring to use reduce would be better. Here is what I have:
const arr = [
{
"id": "vghjnbghjkoijhjnmkjhjk",
"region": "US",
"tags": ["tag1:bvghjhgh","tag2:bvghjkjnm","tag3:vghjbghj"]
},
{
"id": "cvbhyt56789-mnbvghyu76",
"region": "US",
"tags": ["tag1:bvghjhgh"]
},
{
"id": "ghjkjnbhjnbhjkmnhjkmjk",
"region": "US",
"tags": ["tag2:bvghjkjnm"]
},
{
"id": "ghjkjnbhjnbhjkmnhjkmjk",
"region": "US",
"tags": []
},
{
"id": "bghjkjnbghjkjnhjnbhjhj",
"region": "CA",
"tags": ["tag1:bvghjhgh","tag3:vghjbghj"]
}
];
The expected results are as follows, based on a key of tags and a value of tag1:
[
[
{
"id": "vghjnbghjkoijhjnmkjhjk",
"region": "US",
"tags": ["tag1:bvghjhgh","tag2:bvghjkjnm","tag3:vghjbghj"]
},
{
"id": "cvbhyt56789-mnbvghyu76",
"region": "US",
"tags": ["tag1:bvghjhgh"]
},
{
"id": "bghjkjnbghjkjnhjnbhjhj",
"region": "CA",
"tags": ["tag1:bvghjhgh","tag3:vghjbghj"]
}
],
[
{
"id": "ghjkjnbhjnbhjkmnhjkmjk",
"region": "US",
"tags": ["tag2:bvghjkjnm"]
},
{
"id": "ghjkjnbhjnbhjkmnhjkmjk",
"region": "US",
"tags": []
},
]
]
Here is my current function:
function groupData(arr, key, value) {
const grouped = {};
const remaining = [];
for (const obj of arr) {
const index = obj[key].findIndex(elem => elem.includes(value));
if (index > -1) {
const groupByKey = obj[key][index];
if (grouped.hasOwnProperty(groupByKey)) {
grouped[groupByKey].push(obj);
} else {
grouped[groupByKey] = [obj];
}
} else {
remaining.push(obj);
}
}
return [Object.values(grouped).flat(), noMatch];
}
I provide a solution using reduce() and map,but compared with other answers,seems groupby is a better choice
const arr = [
{
"id": "vghjnbghjkoijhjnmkjhjk",
"region": "US",
"tags": ["tag1:bvghjhgh","tag2:bvghjkjnm","tag3:vghjbghj"]
},
{
"id": "cvbhyt56789-mnbvghyu76",
"region": "US",
"tags": ["tag1:bvghjhgh"]
},
{
"id": "ghjkjnbhjnbhjkmnhjkmjk",
"region": "US",
"tags": ["tag2:bvghjkjnm"]
},
{
"id": "ghjkjnbhjnbhjkmnhjkmjk",
"region": "US",
"tags": []
},
{
"id": "bghjkjnbghjkjnhjnbhjhj",
"region": "CA",
"tags": ["tag1:bvghjhgh","tag3:vghjbghj"]
}
];
let mdata = {'tags':'tag1'}
let entries = Object.entries(mdata).flat()
let key = entries[0],value = entries[1]
let result = arr.reduce((a,c) => {
let exists = c[key].some(i => i.startsWith(value + ':'))
let skey = exists ? value: 'other'
let obj = a.find(a => a[skey])
if(obj){
obj[skey].push(c)
}else{
obj = {[skey]:[c]}
a.push(obj)
}
return a
},[])
result = result.map(d => d[value]||d.other)
console.log(result)
A generic groupBy (similar functions exist in popular libraries like LoDash) would help here:
const groupBy = (iterable, keyFn) => {
const groups = new Map();
for (const x of iterable) {
const key = keyFn(x);
let group = groups.get(key);
if (group === undefined) {
groups.set(key, group = []);
}
group.push(x);
}
return groups;
};
Then you have:
const groupData = (arr, key, value) => {
const groups = groupBy(arr, obj =>
obj[key].find(elem => elem.includes(value)));
const noMatch = groups.get(undefined);
groups.delete(undefined);
return [[...groups.values()].flat(), noMatch];
};

Group and count the nested json response - Angular

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:

How to dynamically push an array using spread operator Javascript

[
{
"details": {
"name": "john",
"point": "20"
},
"list": {
"number": "30",
}
},
{
"details": {
"name": "doe",
"point": "25"
},
"list": {
"number": "30",
}
}
]
This is what i am trying to do, i am getting the data from the store and if the response is only one i use data = getData[0].details and if the response is more than one then i push the data using [...data, ...getData[1].details] if there are more than one data how can i achieve. thanks in advance
let data:any = [];
this.store
.pipe(
select(getData),
map((getData,i) => {
if (getData) {
data = [...data, ...getData[i].details]
}
return data;
})
)
I think you want to get an array of the details.
In this case you can say.
let data = [
{
"details": {
"name": "john",
"point": "20"
},
"list": {
"number": "30",
}
},
{
"details": {
"name": "doe",
"point": "25"
},
"list": {
"number": "30",
}
}
];
let details = data.map(a => a.details);
console.log(details);
I think i understand what you mean:
What I would do is map over your getData response and add to the original array on each iteration. It wont matter if there is 1 or many in the getData array:
getData.map(x => $data.push(x.details));

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)

When trying to PUT an object in my Array, this filter function is not removing the old object

The repo: https://github.com/leongaban/api-design-node/tree/master
My Postman collection: https://www.getpostman.com/collections/b5a03b07836ad34b7758
Expected:
Current "lion" characters:
[
{
"id": "1",
"name": "Teemo",
"pride": "LoL",
"age": "1",
"gender": "male"
},
{
"id": "2",
"name": "Nasus",
"pride": "LoL",
"age": "10",
"gender": "male"
}
]
PUT http://localhost:3000/lions/1
The body:
{
"age": "1",
"gender": "female",
"name": "LuLu",
"pride": "LoL"
}
Should return this new list on GET all lions:
[
{
"id": "1",
"name": "LuLu",
"pride": "LoL",
"age": "1",
"gender": "female"
},
{
"id": "2",
"name": "Nasus",
"pride": "LoL",
"age": "10",
"gender": "male"
}
]
Results
[
{
"id": "2",
"name": "Nasus",
"pride": "2",
"age": "2",
"gender": "female"
},
{
"0": { // <-- This should not be here
"id": "1",
"name": "Teemo",
"pride": "1",
"age": "1",
"gender": "female"
},
"age": "1",
"gender": "female",
"name": "LuLu",
"pride": "LoL"
}
]
Full server.js
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const path = require('path')
const port = 3000
app.use(express.static('client'))
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
let lions = []
let id = 0
app.get('/lions', function(req, res) {
console.log('GET lions:', lions)
res.json(lions)
})
app.get('/lions/:id', function(req, res) {
let lion = lions.filter((lion => lion.id === req.params.id))
res.json(lion || {})
})
app.post('/lions', function(req, res) {
id++
const lion = Object.assign({ id: id.toString() }, req.body)
lions.push(lion)
res.json(lion)
});
app.put('/lions/:id', function(req, res) {
const paramId = req.params.id
const updated = req.body
if (updated.id) {
delete updated.id
}
const oldLion = lions.filter((lion => lion.id === paramId))
if (!oldLion) {
res.send()
}
const newLion = Object.assign(updated, oldLion)
console.log('newLion', newLion)
lions = lions.filter(lion => lion.id !== paramId)
lions.push(newLion)
res.json(newLion)
});
app.listen(port, () => console.log(`NODE RUNNING on port: ${port}`))
The PUT function
app.put('/lions/:id', function(req, res) {
const paramId = req.params.id
const updated = req.body
if (updated.id) {
delete updated.id
}
// Here I find the oldLion to replace by id:
const oldLion = lions.filter((lion => lion.id === paramId))
if (!oldLion) {
res.send()
}
// Here I create a new object for the new "lion":
const newLion = Object.assign(updated, oldLion)
console.log('newLion', newLion)
// Here I filter out the old lion:
lions = lions.filter(lion => lion.id !== paramId)
// New lion is pushed in:
lions.push(newLion)
res.json(newLion)
});
One potential issue that can be on the app.put() method, is that when you do const oldLion = lions.filter((lion => lion.id === paramId)) you will get an array as result. Check Array.filter() for more info about this. So, I believe you want to use Array.find() instead of the filter() because later you are calling:
const newLion = Object.assign(updated, oldLion);
And, if oldLion is an array, numeric-properties will be added to the updated object, as you can see on next example:
const updated = {somekey: "somevalue"};
console.log(Object.assign(updated, [{somekey: "updatedValue"}]));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
So, this is generating your unexpected structure, as you have mentioned:
[
{
"id": "2",
"name": "Nasus",
"pride": "2",
"age": "2",
"gender": "female"
},
{
"0": { // <-- This should not be here
"id": "1",
"name": "Teemo",
"pride": "1",
"age": "1",
"gender": "female"
},
"age": "1",
"gender": "female",
"name": "LuLu",
"pride": "LoL"
}
]
UPDATE
However, after reading your other fix commented, and understanding what you are trying to do, maybe it is better to use Array.findIndex() and write your put() method like this:
app.put('/lions/:id', function(req, res)
{
const paramId = req.params.id;
const updated = req.body;
if (updated.id)
delete updated.id;
// Here I find the oldLion to replace by id:
const oldLionIdx = lions.findIndex(lion => lion.id === paramId);
if (oldLionIdx < 0)
res.send();
// Here I update the object with the new "lion" properties:
const newLion = Object.assign(lions[oldLionIdx], updated);
console.log('newLion', newLion);
res.json(newLion);
});

Categories

Resources