Dynamically parsing JSON arrays in JavaScript - 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

Related

How to traverse through a tree like nested data structure of unknown depth in order to find and collect addressable array items?

Say I have an array that looks as such:
[{
"name": "Audiograms",
"folders": [{
"name": "2022"
}, {
"name": "2021"
}, {
"name": "2020"
}]
}, {
"name": "Patient Paperwork"
}, {
"name": "Repairs"
}]
And this array can have an infinite amount of objects and sub-objects, similar to a file tree.
I have an array letting me know the name of the folders I need to access from the root of the object, like:
["Audiograms", "2022"]
I also do not know this value ahead of time, nor do I know how many items are in this array ahead of time.
How would I be able to actually traverse this file tree using the array of names? I wish to do things like maybe pop the matching object out and move it to another part of the file tree.
Thank you!
OP
"I wish to do things like maybe pop the matching object out and move it to another part of the file tree."
In order to achieve follow-up tasks like the above mentioned one, the next provided solution walks the OP's folder structure and collects for each addressable match an object of two references, target and parent, where the former is the reference of the to be found folder-item, and the latter is the reference of its parent folder-item.
The solution got achieved by a recursively implemented reducer function.
function collectAddressableFolderRecursively(collector, folderItem) {
const { name = null, folders = [] } = folderItem;
const {
address: [parentName, childName], result,
} = collector;
if (name === parentName && folders.length) {
const targetFolder = folders
.find(({ name }) => name === childName) ?? null;
if (targetFolder !== null) {
result.push({
target: targetFolder,
parent: folderItem,
});
}
}
result.push(
...folders.reduce(collectAddressableFolderRecursively, {
address: [parentName, childName],
result: [],
})
.result
);
return collector;
}
const folders = [{
name: 'Audiograms',
folders: [{
name: '2022',
folders: [{
name: 'Audiograms',
folders: [{
name: '2022',
}, {
name: 'foo',
}],
}],
}, {
name: '2021',
}, {
name: '2020',
}]
}, {
name: 'Patient Paperwork',
}, {
name: 'Repairs',
folders: [{
name: 'Audiograms',
folders: [{
name: '2022',
}, {
name: 'bar',
}],
}, {
name: 'baz',
}],
}]
const address = ['Audiograms', '2022'];
const { result } = folders
.reduce(collectAddressableFolderRecursively, {
address,
result: [],
});
console.log({ address, result });
.as-console-wrapper { min-height: 100%!important; top: 0; }

How to sort an array of objects by comparing different fields in typescript

So I found examples on how to sort arrays by comparing the same field, but I need to sort them by comparing different fields. For example I have a lists of objects where each object has a field for their name and parent. I want to sort the list so that the people appear next to their parent. Example:
[
{
"name": "Bob",
"parent": "Linda"
},
{
"name": "Charlie",
"parent": "Gregory"
},
{
"name": "Linda",
"parent": "Stacy"
},
{
"name": "Andrew",
"parent": "Gabriel"
},
{
"name": "Gregory",
"parent": "Thomas"
}
]
After sorting I want Bob to be next to Linda and Charlie to be next to Gregory.
Hello #poppo8989: Welcome to Stack Overflow.
Suggestion: If you have control over the data presented in your question, you might consider storing it in a structure that better represents the relationships.
Otherwise, here's a take on solving your problem:
Explore code in TypeScript Playground
type Person = {
name: string;
parent: string;
};
type RelationshipData = {
child?: Person;
parent?: Person;
};
function getRelationships (people: Person[], person: Person): RelationshipData {
return {
child: people.find(p => p.parent === person.name),
parent: people.find(p => p.name === person.parent),
};
}
function getSortedPeople (people: Person[]): Person[] {
const sorted: Person[] = [];
const copy = [...people];
while (copy.length > 0) {
let person: Person | undefined = copy[0];
let done = false;
// set person to furthest ancestor
while (person && !done) {
const {parent} = getRelationships(copy, person);
if (parent) person = parent;
else done = true;
}
// remove from copy array and add to sorted array, repeatedly for each child
while (person) {
copy.splice(copy.indexOf(person), 1);
sorted.push(person);
person = getRelationships(copy, person).child;
}
}
return sorted;
}
function main () {
const people: Person[] = [
{name: 'Bob', parent: 'Linda'},
{name: 'Charlie', parent: 'Gregory'},
{name: 'Linda', parent: 'Stacy'},
{name: 'Andrew', parent: 'Gabriel'},
{name: 'Gregory', parent: 'Thomas'},
];
const sorted = getSortedPeople(people);
console.log(sorted); //=> Linda, Bob, Gregory, Charlie, Andrew
}
main();

How to turn an Array of objects into a nested object?

Hello I try to get this output
{
name: "Saviole",
role: "ceo",
children: [
{
name: "Mary",
role: "supervisorA",
children: [
{name: "Anna", role: "worker"}
]
}, {
name: "Louis",
role: "supervisorB"
}]
}
These are the functions I wrote:
const users = [
{name: "Anna", role: "worker"},
{ name: "Mary", role: "supervisorA" },
{ name: "Louis", role: "supervisorB" },
{ name: "Saviole", role: "ceo" }
];
const recursiveAddToTree = (parent, child, grandChildren, users)=>{
let tree = {};
users.forEach(( user)=>{
if(user.role===parent){
tree ={...user}
} else if(user.role===child){
tree = {...tree, chidren:[...[user]]}
} else {
users.forEach(userChild=>{
if(userChild.role===child){
tree = {...tree, children:[...[{...userChild, chidren: [...[user]]}]]}
}
})
}
})
return tree;
}
const createSchema = users =>{
return recursiveAddToTree("ceo", "supervisorA","worker", users)
}
How do I solve this? I don't understand why it doesn't work as thought
You could take a sorted array where all users are orderd by their role under the direct role above and take an object to get a level of the roles. Then iterate and take an array of level for keeping track of the last user and inser the user according to the role/level.
This approach maintains the given order.
const
users = [
{ name: "Saviole", role: "ceo" },
{ name: "Mary", role: "supervisor" },
{ name: "Anna", role: "worker" },
{ name: "Louis", role: "supervisor" }
],
roles = { ceo: 0, supervisor: 1, worker: 2 },
tree = [],
levels = [tree];
users.forEach(user => {
const level = roles[user.role];
if (!levels[level]) {
const
temp = levels[level - 1],
last = temp[temp.length - 1];
levels[level] = [];
last.children = levels[level];
}
levels[level].push(user);
});
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I saw 2 issue in your code:
the foreach will process the users one by one, finishing by the last one: the ceo. But in your code if the processed user is the ceo your tree object is replaced by the user, so all your previous modifications are erased (I think it's the most important issue)
the roles of the supervisors are "supervisorA" and "supervisorB", but you're checking if the role is "supervisor", it'll never match, you should check if the role start with "supervisor"

Looping through nested data and displaying object properties and values

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);

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