Make a flat object nested in js - javascript

I would like to transform my object into nested object in order to be able to render inputs recursively in react (if input has sub input render below this inputs) Sometimes inputs are in wrong order so I have to transform object like this
const start = {
asdf: {
id: "asdf",
question: "How old are you?",
type: "text"
},
"asdf/zxcv": {
id: "asdf/zxcv",
expect: 18,
question: "Are you male?",
type: "text"
},
"asdf/zxcv/yuio": {
id: "asdf/zxcv/yuio",
expect: "yes",
question: "Do you like videogames?",
type: "text"
},
"asdf/dfgh": {
id: "asdf/dfgh",
expect: 21,
question: "Where do you live?",
type: "text"
},
"asdf/dfgh/fghj": {
id: "asdf/dfgh/fghj",
expect: "Boston",
question: "What's the weather?",
type: "text"
},
qwer: {
id: "qwer",
question: "What is your name?",
type: "text"
},
"qwer/asdf": {
id: "qwer/asdf",
expect: "David",
question: "What is your surname",
type: "text"
}
};
into something like this
const result = {
asdf: {
id: "asdf",
question: "How old are you?",
type: "text",
subs: [
{
id: "asdf/zxcv",
expect: 18,
question: "Are you male?",
type: "text",
subs: [
{
id: "asdf/zxcv/yuio",
expect: "yes",
question: "Do you like videogames?",
type: "text"
}
]
},
{
id: "asdf/dfgh",
expect: 21,
question: "Where do you live?",
type: "text",
subs: [
{
id: "asdf/dfgh/fghj",
expect: "Boston",
question: "What's the weather?",
type: "text"
}
]
}
]
},
qwer: {
id: "qwer",
question: "What is your name?",
type: "text",
subs: [
{
id: "qwer/asdf",
expect: "David",
question: "What is your surname",
type: "text"
}
]
}
};
I can create the first level of objects but I don't know how to add dynamically object to subs if needed
This is my try. It doesn't work.
const createObjToRender = object => {
Object.values(object)
.filter(o => o)
.reduce((obj, el) => {
const idHistory = el.id.split("/");
if (idHistory.length > 1) {
let elToAddSubInput = obj;
for (let i = 0; i <= idHistory.length; i++) {
i === 0 && (elToAddSubInput = elToAddSubInput[idHistory[i]]);
i > 0 && (elToAddSubInput = elToAddSubInput.subs[idHistory[i]]);
}
elToAddSubInput.subs[idHistory.join("/")] = el;
}
if (idHistory.length === 1) {
const id = idHistory[0];
const { question, type } = el;
if (!obj[id]) {
obj[id] = {
id,
question,
type,
subs: {}
};
}
}
}, {});
};

You could use a nested hashtable for building the tree fast:
const start = {
asdf: {
id: "asdf",
question: "How old are you?",
type: "text"
},
"asdf/zxcv": {
id: "asdf/zxcv",
expect: 18,
question: "Are you male?",
type: "text"
},
"asdf/zxcv/yuio": {
id: "asdf/zxcv/yuio",
expect: "yes",
question: "Do you like videogames?",
type: "text"
},
"asdf/dfgh": {
id: "asdf/dfgh",
expect: 21,
question: "Where do you live?",
type: "text"
},
"asdf/dfgh/fghj": {
id: "asdf/dfgh/fghj",
expect: "Boston",
question: "What's the weather?",
type: "text"
},
qwer: {
id: "qwer",
question: "What is your name?",
type: "text"
},
"qwer/asdf": {
id: "qwer/asdf",
expect: "David",
question: "What is your surname",
type: "text"
}
};
// We use a symbol to allow fast lookups while still having a resulting array without it
const lookup = Symbol();
const root = { [lookup]: {}, sub: [] };
// As the ids are part of the values itself, we can ignore the objects keys and directly iterate the values
for(const el of Object.values(start)) {
// Now we traverse down the nested lookup tree
let parent = root;
for(const part of el.id.split("/")) {
// If a certain path doesnt exist yet, set it up
if(!parent[lookup][part])
parent.sub.push(parent[lookup][part] = { [lookup]: {}, sub: [] });
// Dive deeper
parent = parent[lookup][part];
}
// We reached the node were the data belongs, so just assign it here:
Object.assign(parent, el);
}
// you could also get it as an array with root.sub
console.log(root[lookup]);

Related

JSON: how do you add to a field that doesn't have a key?

I have a simple JSON object
simple_chart_config = {
chart: {
container: "#tree-simple"
},
nodeStructure: {
text: { name: "Question" },
children: [
{
text: { name: "Answer 1" }
}
]
}
};
And I'd like to add a new subfield within the first entry on the children array so that the final output is
simple_chart_config = {
chart: {
container: "#tree-simple"
},
nodeStructure: {
text: { name: "Question" },
children: [
{
text: { name: "Answer 1" },
children: [
{
text: { name: "Question 2" }
}
]
}
]
}
};
I've tried several methods, such as
var questionTwoStr = '{"children": [{"text": { "name": "Question 2" }}]}'
var questionTwo = JSON.parse(questionTwoStr);
simple_chart_config.nodeStructure.children[0] = questionTwo;
but I'm having issues working out all of the nested indexes in my head. This is for a tree in treant.js if that context is helpful at all.
I think I'm mostly confused because the place I'm trying to add the new subfield doesn't have a key, which I thought was required for JSON.
There's no need to use JSON here; you can add the object itself:
simple_chart_config.nodeStructure.children[0].children = [{text: { name:"Question 2" }}];

Typescript: Property 'name' does not exist on type 'Employee[]'

I am completely new to typescript, and I'm stumped by this error message: Property 'name' does not exist on type 'Employee[]' Could someone please point out where I'm not applying the "name" type within the Employee array? Thanks.
interface Employee {
id: number;
name: string;
title: string;
}
var employees: Employee[] = [
{ id: 0, name: "Franklin", title: "Software Enginner" },
{ id: 1, name: "Jamie", title: "Human Resources" },
{ id: 2, name: "Henry", title: "Application Designer" },
{ id: 3, name: "Lauren" title: "Software Enginner" },
{ id: 4, name: "Daniel" title: "Software Enginner 2" },
];
function fetchEmployeeName(id : number) {
var employee = employees.filter(
(employee) => employee.id === id
);
// The error occurs when I try to return a "name" under an employee that matched by id.
return employee.name;
}
console.log("Got Employee: "), fetchEmployeeName(3));
filter returns a new array containing all matching items:
[1, 2, 3].filter(i => i === 4)
The above will return an empty array.
What you want to use is find, which will return a single matching item or undefined.
Modify the fetchEmployeeName function to use find:
function fetchEmployeeName(id : number): string | null {
var employee = employees.find(
(employee) => employee.id === id
);
if (employee === undefined) return null;
return employee.name;
}
Try using find instead of filter. Filter returns an array. Find returns a single object. Next time, if using vscode, hover over employee on the first line of fetchEmployeeName, and check its type. Intellisense in vscode will point out to you that employee is clearly an array.
I highly recommend you to use find instead of filter, but if you really want to stick to your approach, you will have to access the only member in the employees array though its index (filter returns an array filled with the elements that meet the specified condition). E.G.:
return employee[0].name
Again, you can solve this particular issue by using filter, since it returns a single element you access without the need of an index (this will allow you to leave the return statement as it is).
there you have it, so what happened, your filter is returning to a new "Employee" that is not defined as an object,my advise is to always try to use pure functions and understand what your return is
interface Employee {
id: number;
name: string;
title: string;
}
var employees: Employee[] = [
{ id: 0, name: "Franklin", title: "Software Enginner" },
{ id: 1, name: "Jamie", title: "Human Resources" },
{ id: 2, name: "Henry", title: "Application Designer" },
{ id: 3, name: "Lauren", title: "Software Enginner" },
{ id: 4, name: "Daniel", title: "Software Enginner 2" },
];
function fetchEmployeeName (id:number, employees: Employee[]){
let employee = null
for (let i = 0, j = employees.length ; i < j; i++) {
if (employees[i].id === id) {
employee = employees[i].name
}
}
return employee
}
console.log(`Got employee 3: ${fetchEmployeeName(3,employees)}`);

run function with uniquie value using javascript / node js

let object=
[
{
id:`01`,
name:`fish`,
type:null,
care:'owner',
},
{
id:`02`,
name:`fish`,
type:'fresh',
care:'peter',
},
{
id:`03`,
name:`fish`,
type:`fresh`,
care:'amy',
},
{
id:`04`,
name:`fish`,
type:`tank`,
care:'abc',
},
{
id:`05`,
name:`animal`,
type:`pet`,
care:'teen',
},,
{
id:`06`,
name:`animal`,
type:`pet`,
care:'ran',
},
{
id:`07`,
name:`animal`,
type:null,
care:'roh',
},
{
id:`08`,
name:`food`,
type:`veg`,
care:'test',
},
{
id:`09`,
name:`food`,
type:null,
care:'dop',
}
]
object.map((value)=>{
console.log(value.name)
// i am calling function here by passing value.name as a parameter
let gotValue = functionName(value.name);
// using type also
if(typeof value.type!=="string"){
// Do some task here with gotValue
}
})
I have this object and i am getting some value from it for ex getting name from it as i want to pass this name to function but the problem is due to repeat of data the function calling again and again is there any possibility i can run function inside map but with unique value any help ?
as my output is getting like this
fish
fish
fish
animal
animal
animal
and this value.name is passing inside my function so its repeating like this
functionName(fish);
functionName(fish);
functionName(fish);
functionName(animal);
functionName(animal);
functionName(animal);
multiple time function is running with same name and getting duplicate values
just need my function run with unique name
functionName(fish)
functionName(animal);
functionName(food);
as i want to stay inside map function because i am performing some task which can only be possible inside map that's why i need unique value
You can use Set which can be used to test if the object with value already exists or not. It will only call the function only once.
let object = [
{
id: `01`,
name: `fish`,
type: null,
},
{
id: `02`,
name: `fish`,
type: `fresh`,
},
{
id: `03`,
name: `fish`,
type: `tank`,
},
{
id: `04`,
name: `animal`,
type: `pet`,
},
{
id: `05`,
name: `animal`,
type: `wild`,
},
{
id: `06`,
name: `animal`,
type: null,
},
{
id: `07`,
name: `food`,
type: `veg`,
},
{
id: `08`,
name: `food`,
type: null,
},
];
const dict = new Set();
object.map((value) => {
if (!dict.has(value.name)) { // Run only if objet with name is not already existed in dict
dict.add(value.name);
console.log(value.name); // For testing
// functionName(value.name);
}
});
If you want to call the function with two filters then you can use some to find the elements in an array. See I've now declared dict as an array
let object = [{
id: `01`,
name: `fish`,
type: null,
},
{
id: `02`,
name: `fish`,
type: `fresh`,
},
{
id: `03`,
name: `fish`,
type: `tank`,
},
{
id: `04`,
name: `animal`,
type: `pet`,
},
{
id: `05`,
name: `animal`,
type: `wild`,
},
{
id: `06`,
name: `animal`,
type: null,
},
{
id: `07`,
name: `food`,
type: `veg`,
},
{
id: `08`,
name: `food`,
type: null,
},
{
id: `09`,
name: `food`,
type: null,
},
{
id: `10`,
name: `fish`,
type: `tank`,
},
];
const dict = [];
object.map((value) => {
const { name, type } = value;
if (!dict.some((obj) => obj.name === name && obj.type === type)) {
// Run only if objet with name is not already existed in dict
dict.push({ name, type });
console.log(name, type); // For testing
// functionName(value.name);
}
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

Can't regroup an array

I do a simple explorer on Angular (there are a list of directories that contain other directories or text files). The question is: I receive the following data from the server ("path" is the path of the folder, ids of parent directories):
[
{
id: "6np5E3yyEISXLNX9muyt",
name: "sec list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
},
{
id: "GnBOclNO1v3n9FW7aGv0",
name: "In aeroport",
path: [""],
},
{
id: "H6AvpwXc49v4oDRWSjym",
name: "Delete",
path: [""],
},
{
id: "LQ73vVoTuw9xd40jMs3j",
name: "Aeroport list",
path: [""],
},
{
id: "X5YNJ6Vco2BtGxNZVsYV",
name: "Bordery words",
path: ["", "GnBOclNO1v3n9FW7aGv0"],
},
{
id: "jWeClRAw55Er8z0Ow9uq",
name: "mail list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
}
];
How can I regroup that into code below? I know recursion is needed, but I can not understand, how to do it right. Help me, please.
[
{
id: "GnBOclNO1v3n9FW7aGv0",
name: "In aeroport",
children: [
{
id: "X5YNJ6Vco2BtGxNZVsYV",
name: "Bordery words",
children: [
{
id: "6np5E3yyEISXLNX9muyt",
name: "sec list",
},
{
id: "jWeClRAw55Er8z0Ow9uq",
name: "mail list",
}
],
}
],
},
{
id: "H6AvpwXc49v4oDRWSjym",
name: "Delete",
},
{
id: "LQ73vVoTuw9xd40jMs3j",
name: "Aeroport list",
},
]
Simple DFS solves the problem, but there are multiple ways to do this. One way is below
var paths = [
{
id: "6np5E3yyEISXLNX9muyt",
name: "sec list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
},
{
id: "GnBOclNO1v3n9FW7aGv0",
name: "In aeroport",
path: [""],
},
{
id: "H6AvpwXc49v4oDRWSjym",
name: "Delete",
path: [""],
},
{
id: "LQ73vVoTuw9xd40jMs3j",
name: "Aeroport list",
path: [""],
},
{
id: "X5YNJ6Vco2BtGxNZVsYV",
name: "Bordery words",
path: ["", "GnBOclNO1v3n9FW7aGv0"],
},
{
id: "jWeClRAw55Er8z0Ow9uq",
name: "mail list",
path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],
}
];
var dfs = function( parentJson , path){
for(var i=0;i<paths.length;i++){
if(paths[i].path.join("") == path ){
var child = {id:paths[i].id,name:paths[i].name,children:[]}
parentJson.push(child)
dfs(child.children,path+paths[i].id)
}
}
}
var json = [];
dfs(json,"")
console.log(json)
You could do this in two phases:
First make a nested object structure where the children properties are not arrays, but objects, where the object keys are the id values. That way you can quickly navigate in that structure with a given path, and extend/deepen it at the same time.
In a final phase, you can use recursion to walk through that tree structure to convert those children properties to arrays.
Here is how that looks:
function makeTree(data) {
// drill down the object structure, where children properties
// are nested objects with id values as keys.
let result = {}; // root of the tree data structure
for (let {id, name, path} of data) {
Object.assign(path.slice(1).concat(id).reduce((acc, key) => {
if (!acc.children) acc.children = {};
if (!acc.children[key]) acc.children[key] = {};
return acc.children[key];
}, result), { id, name });
}
return (function unkey(node) {
// Convert children objects to arrays
if (node.children) node.children = Object.values(node.children).map(unkey);
return node;
})(result);
}
let data = [{id: "6np5E3yyEISXLNX9muyt",name: "sec list",path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],},{id: "GnBOclNO1v3n9FW7aGv0",name: "In aeroport",path: [""],},{id: "H6AvpwXc49v4oDRWSjym",name: "Delete",path: [""],},{id: "LQ73vVoTuw9xd40jMs3j",name: "Aeroport list",path: [""],},{id: "X5YNJ6Vco2BtGxNZVsYV",name: "Bordery words",path: ["", "GnBOclNO1v3n9FW7aGv0"],},{id: "jWeClRAw55Er8z0Ow9uq",name: "mail list",path: ["", "GnBOclNO1v3n9FW7aGv0", "X5YNJ6Vco2BtGxNZVsYV"],}];
console.log(makeTree(data));
Note that the first value of the path value is never used. It seems to always be the empty string.

Javascript Array Manipulation - Is there a more declarative approach?

Thanks for taking a look here. I'm working with an API, and need to change the format of the data. Here's an example of the return data:
data: [
{
status: "planned work",
name: "123"
},
{
status: "all good",
name: "nqr"
}
];
Each train line has a name like "123" or "nqr", and I want to split each train into their own objects so that it would look something like this:
data: [
{
status: "planned work",
name: "1"
},
{
status: "planned work",
name: "2"
},
{
status: "planned work",
name: "3"
},
{
status: "all good",
name: "n"
},
{
status: "all good",
name: "q"
},
{
status: "all good",
name: "r"
}
];
I have some working code which splits the name and uses nested .forEach loops to push items to an array. Working code:
function formatLinesData(lines) {
var trains = [];
lines.forEach( line => {
line.name.split("").forEach(train => {
trains.push({name: train, status: line.status});
});
});
return trains;
}
Is there a way to accomplish this without the nested loops? Looking for an elegant solution if you have one.
Thanks
You might do as follows;
var data = [
{
status: "planned work",
name: "123"
},
{
status: "all good",
name: "nqr"
}
],
newData = [].concat(...data.map(o => o.name.split("").map(c => ({status: o.status, name: c}))));
console.log(newData);
You can use reduce - initialize it with an empty array, and iterate over the data
array using your logic.
data.reduce((prev,curr) => {
for (let i=0; i<curr.name.length; i++) {
prev.push({ name : curr.name[i], status : curr.status});
}
return prev;
},[]);

Categories