Looping through nested data and displaying object properties and values - javascript

In my React app, I'm looking for a clean way to loop through the following dynamic data structure and display the object properties and values.
Sample data:
data: {
company: [
{
company_name: "XYZ Firm",
company_email: "hello#xyz.com",
company_phone: 91982712,
}
],
shareholders: [
{
shareholder_name: "Lin",
percentage: 45
},
{
shareholder_name: "Alex",
percentage: 10
},
],
employees: [
{
employee_name: "May",
employee_email: "may#xyz.com"
},
]
}
The output I want is:
company_name: XYZ Firm
company_email: hello#xyz.com
company_phone: 91982712
shareholder_name: Lin
shareholder_percentage: 45
shareholder_name: Alex
shareholder_percentage: 10
employee_name: May
employee_email: may#xyz.com
This is what I've tried so far:
//data contains the entire object
const profileInfo = Object.keys(data).map(key => {
let profileSection = [];
for (let values of data[key]) { //retrieve the objects of each "section" e.g., company, shareholders
Object.keys(values).map(key => {
profileSection.push(<p>{key}: {values[key]}</p>);
})
}
return profileSection;
})
I'm able to achieve the intended results but I'm not sure if it's the best solution in terms of performance. Having nested Object.keys().mapseems a bit off to me.
Note: User will be able to add more shareholders/employees.

Here is a somewhat shorter version using Object.values() and Object.entries().
var data = { company: [ { company_name: "XYZ Firm", company_email: "hello#xyz.com", company_phone: 91982712, } ], shareholders: [ { shareholder_name: "Lin", percentage: 45 }, { shareholder_name: "Alex", percentage: 10 }, ], employees: [ { employee_name: "May", employee_email: "may#xyz.com" }, ] };
let profileInfo = [];
Object.values(data).flat().forEach((item) => {
Object.entries(item).forEach(([key, value]) => {
profileInfo.push(key + ": " + value);
});
});
console.log(profileInfo);

Related

Object gets duplicated inside array

This is the result I want to achieve
dataset: [
dataset: [
{
seriesname: "",
data: [
{
value: "123",
},
{
value: "123",
},
]
},
]
]
My problem right now is that the second dataset gets duplicated.
This is how I am setting it (val is an integer and allYears is an array of integers):
this.grphColumn.dataSource.dataset[0].dataset = this.allYears.map(el => {
return {
seriesname: "Planned",
data: [{value: val}, {value: val}]
}
});
How can I make it so the dataset doesn't get duplicated?
You have to map the values separately, if you dont want the seriesName to be Repeated..
const yearsMap = this.allYears.map((el) => { return { value: el } });
this.grphColumn.dataSource.dataset[0].dataset = {
seriesname: "Planned",
data: yearsMap
}

Dynamically parsing JSON arrays in JavaScript

I'm trying to parse a JSON file stored locally on my machine in JavaScript in discord.js (v12). This JSON has several keys and values:
{
"name": "Robert",
"rank": "Owner",
"hobbies": [{
"id": 1,
"name": "gaming"
}, {
"id": 2,
"name": "listening to music"
}, {
"id": 3,
"name": "vibing"
}, {
"id": 4,
"name": "driving down the highway"
}],
"roles": [{
"id": 1,
"name": "Founder"
}, {
"id": 2,
"name": "Premium Member"
}]
}
I want to send the above in a message on Discord as follows:
name: Robert
rank: Owner
hobbies: gaming, listening to music, vibing, driving down the highway
roles: Founder, Premium Member
I also want this to be dynamic. Meaning my code should adapt if a new key and value is added to the current set.
With the current code used, this is my result:
name: Robert
rank: Owner
hobbies: gaming, listening to music, vibing, driving down the highway
This is my current code:
let noted = ``
var raw = fs.readFileSync(name)
var obj = JSON.parse(raw)
for (var item in obj) {
if (obj[item] instanceof Object) {
for (var i in obj.hobbies) {
noted += `${obj.hobbies[i].name}, `
}
} else {
noted += `${item}: ${obj[item]}\n`
noted += `hobbies: `
}
}
message.channel.send(noted)
The variable name is const name = require("./names.json"); at the top of the code.
This code works fine with name, rank and hobbies.
roles has to be manually checked in the for loop if I want it to be visible. My goal is to cause any new keys to be added to be automatically detected and added into the noted variable.
I've seen something similar done using map(), but I tried it without getting anywhere good. This is rather sloppy code as well but I'm not interested in keeping it clean.
You could do something like this with map and join:
const obj = {"name":"Robert","rank":"Owner","hobbies":[{"id":1,"name":"gaming"},{"id":2,"name":"listening to music"},{"id":3,"name":"vibing"},{"id":4,"name":"driving down the highway"}],"roles":[{"id":1,"name":"Founder"},{"id":2,"name":"Premium Member"}]};
const noted = Object.entries(obj)
.map(([key, val]) =>
`${key}: ${
val instanceof Array ? val.map(x => x.name).join(', ') : val
}`)
.join('\n');
console.log(noted);
Here is an iterative solution using object-scan.
I find it a bit easier to read, but most importantly it is very flexible as to which keys you want to traverse.
// const objectScan = require('object-scan');
const myData = { name: 'Robert', rank: 'Owner', hobbies: [{ id: 1, name: 'gaming' }, { id: 2, name: 'listening to music' }, { id: 3, name: 'vibing' }, { id: 4, name: 'driving down the highway' }], roles: [{ id: 1, name: 'Founder' }, { id: 2, name: 'Premium Member' }] };
const convert = (data) => {
const r = objectScan(['*', '*[*].name'], {
reverse: false,
filterFn: ({ isLeaf, context, key, value }) => {
if (isLeaf) {
if (!(key[0] in context)) {
context[key[0]] = value;
} else {
context[key[0]] += `, ${value}`;
}
}
}
})(data, {});
return Object.entries(r).map(([k, v]) => `${k}: ${v}`).join('\n');
};
console.log(convert(myData));
/* =>
name: Robert
rank: Owner
hobbies: gaming, listening to music, vibing, driving down the highway
roles: Founder, Premium Member
*/
.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

Updated nested object by matching ID

I have an array with nested objects that I need to update from another array of objects, if they match.
Here is the data structure I want to update:
const invoices = {
BatchItemRequest: [
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "11110" },
},
Amount: 2499,
},
],
},
},
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10111" },
},
Amount: 2499,
},
],
},
},
],
};
Here is the array of objects I want to update it from:
const accounts = [
{ AccountCode: "10110", Id: "84" },
{ AccountCode: "11110", Id: "5" },
{ AccountCode: "10111", Id: "81" },
];
I want to update invoices, using accounts, by inserting Id if AccountCode matches, to get the following structure:
const invoices = {
BatchItemRequest: [
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110", Id: "84" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "11110", Id: "5" },
},
Amount: 2499,
},
],
},
},
{
bId: "bid10",
Invoice: {
Line: [
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10110", Id: "84" },
},
},
{
SalesItemLineDetail: {
ItemAccountRef: { AccountCode: "10111", Id: "81" },
},
Amount: 2499,
},
],
},
},
],
};
I have tried various methods, such as the following:
const mapped = invoices.BatchItemRequest.map((item1) => {
return Object.assign(
item1,
accounts.find((item2) => {
return item2 && item1.Invoice.Line.ItemAccountRef.AccountCode === item2.AccountCode;
})
);
});
Problem with this approach (it doesn't work as I think I need to do another nested map), but it also creates a new array, only including the nested elements of invoices.
Does anyone know a good approach to this?
This isn't the cleanest of code but it gets the job done:
function matchInvoiceWithAccount(invoices, accounts) {
const mappedInvoices = invoices.BatchItemRequest.map((request) => {
// Shouldn't modify input parameter, could use Object.assign to create a copy and modify the copy instead for purity
request.Invoice.Line = request.Invoice.Line.map((line) => {
const accountCode = line.SalesItemLineDetail.ItemAccountRef.AccountCode;
// If accounts was a map of AccountCode to Id you would't need to search for it which would be more effective
const account = accounts.find((account) => account.AccountCode === accountCode);
if (account) {
line.SalesItemLineDetail.ItemAccountRef.Id = account.Id;
}
return line;
});
return request;
});
return {
BatchItemRequest: mappedInvoices,
};
}
What you could and probably should do to improve this is to not modify the input parameters of the function, but that requires that you in a better way copy the original, either using Object.assign or spread operator.
At first, it will be good to create Map from your accounts array. We will go one time for array with O(n) and then will read ids by code with O(1). And nested fors is O(m*n), that will be much more slower at big arrays.
const idsByAccountCodes = new Map();
accounts.forEach((data) => {
idsByAccountCodes.set(data.AccountCode, data.Id);
})
or shorter:
const idsByAccountCode = new Map(accounts.map((data) => [data.AccountCode, data.Id]))
then if you want to mutate original values you can go through all nesting levels and add values
for ( const {Invoice:{ Line: line }} of invoices.BatchItemRequest){
for ( const {SalesItemLineDetail: {ItemAccountRef: item}} of line){
item.Id = idsByAccountCodes.get(item.AccountCode) || 'some default value'
// also if you don't have ids for all codes you need to define logic for that case
}
}
If you don't need to mutate original big object "invoices" and all of nested objects, then you can create recursive clone of if with something like lodash.cloneDeep

Omitting one or multiple values in javascript using lodash

I have a complex structure and I want to omit some properties from this structure for final value
let ListofWorlds = {
listOfCountries: [{
add: [{
id: 1,
updated: {
areacode: 123,
city: {
city: {'Austrailia'},
houses: {1000}
}
}
}], remove: []
}]
}
I want to omit city property from this structure and need this
let ListofWorlds = {
listOfCountries: [{
add: [{
id: 1,
updated: {
areacode: 123
}
}], remove: []
}]
}
This is what I have tried
let newListOfWorls = _.map(ListofWorlds, function (worlds) {
return _.omit(worlds, ['city']); })
Appreciate the help and knowledge
This is what i have tried.
let ListofWorlds = {
listOfCountries: [{
add: [{
id: 1,
updated: {
areacode: 123,
city: {
city: 'Austrailia',
houses: 1000
}
}
}], remove: []
}]}
const newList = ListofWorlds.listOfCountries.map(arr=>{
arr.add.forEach((item,index)=>{
arr.add[index] = _.omit(item,'updated.city')
})
return arr
})
Probably not the best way to do it, but hey it works, and why your code doesn't work probably you mapped an Object ListofWorlds and you need to be specific which field you want to be omitted

Build JS arrays by key into one - find a best solution

What's the best solution to mapping 2 multiple arrays to build one by key?
I have 1 array with users who have their profile data like
var users = [{id:5, name:'Alex'}, {id:17, name:'Tom'}, {id:11, name:'John'}];
Also I have another one array of cars with key user_id To determine which machine belongs to which user.
var cars = [{id:333, name:'Nissan', user_id:11}, {id:444, name:'Toyota', user_id:17}, {id:555, name:'BMW', user_id:999}];
So we can see that Tom have Toyota and John have Nissan.
So result should be
a new array with mapped result
[{
"profile": {
"id": 17,
"name": "Tom"
},
"car": {
"id": 444,
"name": "Toyota",
"user_id": 17
}
}, {
"profile": {
"id": 11,
"name": "John"
},
"car": {
"id": 333,
"name": "Nissan",
"user_id": 11
}
}]
My solution is use forEach throw users and sub forEach throw cars and there compare user.id with car.user_id
https://jsfiddle.net/r7qwke1f/37/
You could use a two loop approach instead of a nested loop approach by collecting first all users in a hash table anbd then iterate all cars and if a user is available, then create a new result set.
var users = [{ id: 5, name: 'Alex' }, { id: 17, name: 'Tom' }, { id: 11, name: 'John' }],
cars = [{ id: 333, name: 'Nissan', user_id: 11 }, { id: 444, name: 'Toyota', user_id: 17 }, { id: 555, name: 'BMW', user_id: 999 }],
hash = {},
result = [];
users.forEach(function (user) {
hash[user.id] = user;
});
cars.forEach(function (car) {
if (hash[car.user_id]) {
result.push({ profile: hash[car.user_id], car: car });
}
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another solution
const mappedUsersCars = users.map((user) => ({
profile: user,
car: cars.filter((car) => car.user_id === user.id)[0]
}))
You can use reduce() and find() methods to get desired result.
var users = [{id:5, name:'Alex'}, {id:17, name:'Tom'}, {id:11, name:'John'}];
var cars = [{id:333, name:'Nissan', user_id:11}, {id:444, name:'Toyota', user_id:17}, {id:555, name:'BMW', user_id:999}];
var r = users.reduce(function(r, e) {
var car = cars.find(a => a.user_id == e.id);
if(car) r.push({profile: e, car: car});
return r;
}, [])
console.log(r)
There are basically two methods you would want to use. You want to map the users to the cars, so you want to find a car for the user you are referring to
const result = users.map((user) => {
const car = cars.find(car => car.user_id === user.id);
return {
profile: user,
car,
}
})

Categories

Resources