I have a JSON variable in React like this:
var newTreeData_2 = {
name: current_name,
img: current_img,
uuid: uuid.v4(),
children: [
{
name: "Edit and save",
img:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: uuid.v4()
},
{
name: "add a new one",
img:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: uuid.v4()
}
]
};
Each uuid.v4() is a unique ID.
What I want to do is to make a function which takes in a UUID, and should append a new object children:[{}] to where the UUID is located at. For example, if **my uuid matches the uuid.v4() on line 10 of the code snippet, it should append a JSON object so the end result looks like:
var newTreeData_2 = {
name: current_name,
img: current_img,
uuid: uuid.v4(),
children: [
{
name: "Edit and save",
img:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: uuid.v4(),
chilren: [{}]
},
{
name: "add a new one",
img:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: uuid.v4()
}
]
};
this recursive function find object with target UUID and add children to them
var newTreeData = {
name: 'a',
img: 'b',
uuid: 1234,
children: [{
name: "Edit and save",
img: "https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: 12345,
children: [{
name: "Edit and save",
img: "https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: 123
}]
},
{
name: "add a new one",
img: "https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: 132456
}
]
};
function findUUID(targetObject, uuid) {
var children = targetObject.children;
if (!!children === false) return;
var target = children.filter(x => {
if (x.uuid === uuid) return x;
});
if (target.length === 0) {
children.map(x => findUUID(x, uuid));
} else {
if (!!target[0].children && target[0].children.length !== 0) {
target[0].children.push({});
console.log(target);
} else {
target[0].children = [{}];
console.log(target);
}
}
}
findUUID(newTreeData, 132456);
As I understand it, you want to add an object if the child's uuid is equal to a given uuid.
If that's what you want, you can loop through each child, test if it's uuid is equal to the given uuid, and if it is, add [{}].
function addJSONObjct(obj, uuid) {
for (i = 0; i < obj.children.length; i ++) {
if (obj.children[i].uuid === uuid) {
obj.children[i].children=[{}];
}
}
}
The exact code i have to solve this problem:
findUUID(targetObject, uuid2, name, img, details) {
var targetIsFound = false;
var target = "";
console.log("Parent TreeData UUID" + targetObject.uuid);
console.log("UUID we are trying to match" + uuid2);
if (targetObject.uuid == uuid2) {
targetIsFound = true;
target = targetObject;
}
console.log(targetIsFound, target);
if (targetIsFound == false) {
console.log(
"About to start recursion, this is the target Object: ",
targetObject
);
if (targetObject.children === undefined) {
console.log("we should exit here");
} else {
targetObject.children.map(x => this.findUUID(x, uuid2));
}
} else {
if (target.name == "Edit and save") {
target.children = [
{
name: "Edit and save",
img:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: uuid.v4()
},
{
name: "Edit and save",
img:
"https://cdn3.iconfinder.com/data/icons/harmonicons-06/64/plus-circle-512.png",
uuid: uuid.v4()
}
];
}
this.setState({ treeData: this.state.treeData });
console.log(this.state.treeData);
}
}
name, img and details are just parameters to fill the data with the uuids matched. The reason I named the matching uuid "uuid2" is because I imported the uuid package and there can be conflicts.
Jeff and Barzin's code work, If your data is like mine you have to run recursion on Jeff's method. My code is different from Barzin's in that I am doing an error check: if(targetObject.children === undefined) to make sure I don't continue to perform recursion if there is nothing to perform it with. By the way, this method puts assumption that the UUID we are trying to find definitely exists in targetObject.
Related
I have this array of JSON objects:
and I want to add a unique ID (string) to each entry, like this:
let myTree = [
{
text: 'Batteries',
id: '0',
children: [
{
text: 'BatteryCharge',
id: '0-0'
},
{
text: 'LiIonBattery',
id: '0-1'
}
]
},
{
text: 'Supplemental',
id: '1',
children: [
{
text: 'LidarSensor',
id: '1-0',
children: [
{
text: 'Side',
id: '1-0-0'
},
{
text: 'Tower',
id: '1-0-1'
}
]
}
]
}
]
I just can't think of the right logic to achieve this. I have written this recursive function, which obviously does not achieve what I want:
function addUniqueID(tree, id=0) {
if(typeof(tree) == "object"){
// if the object is not an array
if(tree.length == undefined){
tree['id'] = String(id);
}
for(let key in tree) {
addUniqueID(tree[key], id++);
}
}
}
addUniqueID(myTree);
How can I solve this problem?
Instead of using a number/id in the recursive function I build a string.
let myTree = [{
text: 'Batteries',
children: [{
text: 'BatteryCharge'
},
{
text: 'LiIonBattery'
}
]
},
{
text: 'Supplemental',
children: [{
text: 'LidarSensor',
children: [{
text: 'Side'
},
{
text: 'Tower'
}
]
}]
}
];
function addUniqueID(arr, idstr = '') {
arr.forEach((obj, i) => {
obj.id = `${idstr}${i}`;
if (obj.children) {
addUniqueID(obj.children, `${obj.id}-`);
}
});
}
addUniqueID(myTree);
console.log(myTree);
I hope you are well.
why don't you consider using uuid?
In node there is the uuid module which you can use to generate unique identifiers, I share a base example:
install:
npm install uuid
npm i --save-dev #types/uuid
code:
import {v4 as uuid} from 'uuid';
let _id = uuid();
So I have been tasked with making a basic workflow engine. I have it checking the config file for dependencies and then performing the next function. what I would like to is check to see if the element already exists and not push it to the output array. I have tried .includes() and .indexOf() but I cant seem to get them to work.
const TestWorkflowConfig = {
insertDetails: {
dependencies: [],
workflow: { name: "updateRow", id: 10 },
description: "This is a test function where details need to be entered (row update)",
data: {},
taskName: "insertDetails",
},
detailsConfirmed: {
{ insertDetails: { isRequired: true; } }
dependencies: ["insertDetails"],
workflow: { name: "updateRow", id: 10; },
description: "this is to confirm details (update row status)",
data: {},
taskName: "detailsConfirmed",
},
sendConfirmationEmail: {
dependencies: ["detailsConfirmed"],
workflow: { name: "sendEmail", id: 8; },
description: "this is a test email to send out to confirm details (send email workflow)",
data: { name: "james", email: "james#email.com", body: "this is a test email please ignore"; },
taskName: "sendConfirmationEmail",
},
};
const taskQueue = [
{
"processName": "sendConfirmationEmail",
"isComplete": false
},
{
"processName": "detailsConfirmed",
"isComplete": false
},
{
"processName": "insertDetails",
"isComplete": true
}
];
const workflowTaskQueue = [];
const config = TestWorkflowConfig;
const completedJobs = [];
for (const element of taskQueue) {
if (element.isComplete === true) {
completedJobs.push(element.processName);
}
}
for (const element in config) {
if (config[element].dependencies <= 0) {
// I would like to check if the "config[element]" is in the completedJobs array and only push if it is not there.
workflowTaskQueue.push({ json: config[element] });
} else {
const dependencies = config[element].dependencies;
const dep = [];
for (const el of dependencies) {
dep.push(el);
const result = dep.every((i) => completedJobs.includes(i));
if (result === true) {
// and again I would like to check if the "config[element]" is in the completedJobs array and only push if it is not there
workflowTaskQueue.push({ json: config[element] });
}
}
console.log("taskQueue: " + taskQueue);
console.log("completedJobs: " + completedJobs);
console.log("output:" + workflowTaskQueue);
}
}
as always any help is greatly appreciated.
I have an array of objects that have deeply nested children and sometimes children within children. I am attempting to handle this recursively, but I am getting stuck.
The goal of the function is to return a single data object that matches the id.
My Data looks like this:
data: [
{
id: 'RAKUFNUBNY00UBZ40950',
name: 'Grade 1 Cover',
activityId: 'RAKUFNUBNY00UBZ40950',
nodeType: 'activity',
suppressed: false,
hidden: false
},
{
children: [
{
id: 'SLWDYEQHTZAFA3ALH195',
name: 'Build Background Video',
activityId: 'SLWDYEQHTZAFA3ALH195',
nodeType: 'activity',
suppressed: false,
hidden: false,
assetReference: {
referenceId: 'UWFHA5A1E0EGKCM0W899',
assetType: 'image'
}
},
{
children: [
{
id: 'HQUCD2SSRKMYC2PJM636',
name: 'Eat or Be Eaten Splash Card',
activityId: 'HQUCD2SSRKMYC2PJM636',
nodeType: 'activity',
suppressed: false,
hidden: true
},
{
children: [
{
id: 'ZDTWEZFL13L8516VY480',
name: 'Interactive Work Text: Eat or Be Eaten',
activityId: 'ZDTWEZFL13L8516VY480',
nodeType: 'activity',
suppressed: false,
hidden: true,
defaultLaunchMode: 'modal'
}
],
My attempt at solving this is like this:
findNode(id, currentNode) {
console.log('id', id);
console.log('findNode', currentNode);
var i, currentChild, result, counter;
counter = 0;
console.log('first conditional statement', currentNode);
if (id && currentNode.id === id) {
return currentNode[0];
} else {
counter++;
// Use a for loop instead of forEach to avoid nested functions
// Otherwise "return" will not work properly
console.log('counter', counter);
console.log('currentNode', currentNode[counter]);
console.log('currentNode Children', currentNode.children);
for (i = counter; i < currentNode.children.length; i += 1) {
console.log(currentNode[i].children[i]);
currentChild = currentNode[i].children[i];
// Search in the current child
result = this.findNode(id, currentChild);
// Return the result if the node has been found
if (result !== false) {
return result;
}
}
// The node has not been found and we have no more options
return false;
}
}
The code above fails because I having an extremely difficult time keeping track of a counter to loop through everything.
I also added a sample picture of my data output to give you a better example of how my data is structured. Any help will be greatly appreciated.
You shouldn't need a counter to locate a single node with a matching id. Try this simpler approach:
function findNode (id, array) {
for (const node of array) {
if (node.id === id) return node;
if (node.children) {
const child = findNode(id, node.children);
if (child) return child;
}
}
}
It will return undefined if there is no match.
To avoid the need for manual iteration, you might consider using an array method like reduce instead - return the accumulator if it's truthy (that is, an object was found already), or return the object being iterated over if the ID matches, or recursively iterate over the object's children to find a match.
const data=[{id:'RAKUFNUBNY00UBZ40950',name:'Grade 1 Cover',activityId:'RAKUFNUBNY00UBZ40950',nodeType:'activity',suppressed:!1,hidden:!1},{children:[{id:'SLWDYEQHTZAFA3ALH195',name:'Build Background Video',activityId:'SLWDYEQHTZAFA3ALH195',nodeType:'activity',suppressed:!1,hidden:!1,assetReference:{referenceId:'UWFHA5A1E0EGKCM0W899',assetType:'image'}},{children:[{id:'HQUCD2SSRKMYC2PJM636',name:'Eat or Be Eaten Splash Card',activityId:'HQUCD2SSRKMYC2PJM636',nodeType:'activity',suppressed:!1,hidden:!0},{children:[{id:'ZDTWEZFL13L8516VY480',name:'Interactive Work Text: Eat or Be Eaten',activityId:'ZDTWEZFL13L8516VY480',nodeType:'activity',suppressed:!1,hidden:!0,defaultLaunchMode:'modal'}],}],}],}]
function findId(id, arr) {
return arr.reduce((a, item) => {
if (a) return a;
if (item.id === id) return item;
if (item.children) return findId(id, item.children);
}, null);
}
console.log(findId('HQUCD2SSRKMYC2PJM636', data));
If your ids are unique and finding an object by id is a common task, you might want to consider creating a lookup object to improve performance. Creating the lookup object is an O(n) task; afterwards, looking up an object by id is O(1).
const data = [ { id: 'RAKUFNUBNY00UBZ40950', name: 'Grade 1 Cover', activityId: 'RAKUFNUBNY00UBZ40950', nodeType: 'activity', suppressed: false, hidden: false }, { children: [ { id: 'SLWDYEQHTZAFA3ALH195', name: 'Build Background Video', activityId: 'SLWDYEQHTZAFA3ALH195', nodeType: 'activity', suppressed: false, hidden: false, assetReference: { referenceId: 'UWFHA5A1E0EGKCM0W899', assetType: 'image' } }, { children: [ { id: 'HQUCD2SSRKMYC2PJM636', name: 'Eat or Be Eaten Splash Card', activityId: 'HQUCD2SSRKMYC2PJM636', nodeType: 'activity', suppressed: false, hidden: true }, { children: [ { id: 'ZDTWEZFL13L8516VY480', name: 'Interactive Work Text: Eat or Be Eaten', activityId: 'ZDTWEZFL13L8516VY480', nodeType: 'activity', suppressed: false, hidden: true, defaultLaunchMode: 'modal' } ] } ] } ] } ];
const lookup = {};
const registerIds = a => {
a.forEach(o => {
if ('id' in o) {
lookup[o.id] = o;
} else if ('children' in o) {
registerIds(o.children)
}
});
}
registerIds(data);
console.log(lookup)
Sorry for my two cents, just want to add a universal method that includes nested arrays
const cars = [{
id: 1,
name: 'toyota',
subs: [{
id: 43,
name: 'supra'
}, {
id: 44,
name: 'prius'
}]
}, {
id: 2,
name: 'Jeep',
subs: [{
id: 30,
name: 'wranger'
}, {
id: 31,
name: 'sahara'
}]
}]
function searchObjectArray(arr, key, value) {
let result = [];
arr.forEach((obj) => {
if (obj[key] === value) {
result.push(obj);
} else if (obj.subs) {
result = result.concat(searchObjectArray(obj.subs, key, value));
}
});
console.log(result)
return result;
}
searchObjectArray(cars, 'id', '31')
searchObjectArray(cars, 'name', 'Jeep')
I hope this helps someone
So in an example like this I'm trying to print out the names that don't have null in the 'information'
let files = [
{
name: 'untitled',
information: null
},
{
name: 'folder'
information: 'has storage'
},
{
name: 'new folder',
information: 'has 42 items'
},
The code that I've been trying to use is this one but it doesn't work when I'm trying to print out the names of the folders that don't have null
let info = files.filter((a) => {
if (a.information !== null )
return a
});
console.log(info)
When i put console.log(info.length) to see if it's actually taking in, how many of the items don't have the null in it. It does count the items but when I try to see if I can print out their names it only prints undefined
is there another way to do this?
filter return an array , if you want to print the name you again need to iterate over the array
let files = [{
name: 'untitled',
information: null
},
{
name: 'folder',
information: 'has storage'
},
{
name: 'new folder',
information: 'has 42 items'
}
]
let info = files.filter((a) => {
return a.information !== null;
}).forEach(function(item) {
console.log(item.name)
})
If you want only the name of the folders. Use map to extract just that, as seen below:
let files = [{
name: 'untitled',
information: null
},
{
name: 'folder',
information: 'has storage'
},
{
name: 'new folder',
information: 'has 42 items'
}
]
let info = files.filter((a) => {
return a.information !== null;
}).map((item)=> {
// Get only the names of the folder
return item.name;
});
console.log(info);
Try the following code if it helps.
let files = [
{
name: 'untitled',
information: null
},
{
name: 'folder'
information: 'has storage'
},
{
name: 'new folder',
information: 'has 42 items'
};
var getFilesWithInfo = function () {
var array = [];
for(var i=0; i<files.length;i++){
if(files[i].information){
array.push(files[i]
}
return array;
}
}
console.log(getFilesWithInfo());
You can do this in two simple steps with filter and map.
const files = [{
name: 'untitled',
information: null
},
{
name: 'folder',
information: 'has storage'
},
{
name: 'new folder',
information: 'has 42 items'
}
];
const fileNames = files
.filter(f => f.information !== null)
.map(f => f.name);
console.log(fileNames);
I wish I could give this a more descriptive title, but I don't really know the name of what I am trying to do. I have a JSON list in angular that looks like this:
$scope.users =
{
// list name and the "title" must be the same
Guest:
{
title: 'Guest',
list:
[
{ id: "0", name: "Stephen" },
{ id: "1", name: "Mitch"},
{ id: "2", name: "Nate"},
{ id: "3", name: "Rob" },
{ id: "4", name: "Capt. Jack"},
{ id: "5", name: "Herman" }
]
},
Admin:
{
title: 'Admin',
list:
[]
}
};
And I need to dynamically evaluate a string (either "Guest" or "Admin" or any other user-group that hasn't been created) in order to move a user from one user-group to another.
The function I am working with looks like:
$scope.moveUser = function(fromId, toId, index) {
scope.users.toId.list.push(scope.users.fromId.list[index]);
scope.users.fromId.list.splice(index, 1);
};
with "fromId" and "toId" being strings that evaluate to the name of a user-group ("Admin" or "Guest"). Right now, the function is trying to find a JSON field called "toId" and errors when it can't find any. How would I evaluate the string first so that if the toId == "Guest" and the fromId == "Admin", my function becomes:
scope.users.Guest.list.push(scope.users.Admin.list[index]);
scope.users.Admin.list.splice(index, 1);
change your $scope.moveUser function to
$scope.moveUser = function(fromId, toId, index) {
$scope.users[toId].list.push($scope.users[fromId].list[index]);
$scope.users[fromId].list.splice(index, 1);}
it is really work
If I understand correctly:
$scope.moveUser = function(fromId, toId, index) {
if (users.hasOwnProperty(fromId) && users.hasOwnProperty(toId)) {
scope.users.toId.list.push(scope.users.fromId.list[index]);
scope.users.fromId.list.splice(index, 1);
return true;
} else {
return false;
}
};