Using a string path to set nested array data
Use the reference operator to get the successive existing arrays:
$temp = &$data;
foreach($exploded as $key) {
$temp = &$temp[$key];
}
$temp = $value;
unset($temp);
Set state value using string path key from deeply nested object
You can use the solution in here dynamically-set-property-of-nested-object in your handleChange method like this:
// set method copied from the above link
function set(obj, path, value) {
var schema = obj;
var pList = path.split('.');
var len = pList.length;
for(var i = 0; i < len-1; i++) {
var elem = pList[i];
if( !schema[elem] ) schema[elem] = {}
schema = schema[elem];
}
schema[pList[len-1]] = value;
}
...
const handleChange = (prop) => (e) => {
let value;
if (typeof e.target.value === "number") {
value = parseInt(e.target.value);
}
if (typeof e.target.value === "string") {
value = e.target.value;
}
setPayload((prevState) => {
const newState = {...prevState};
set(newState, prop, value);
return newState;
})
};
....
onChange={handleChange("downloadConfiguration.product.initialDownloadLength")}
Create nested object from array of objects based on path property
I'll show you a solution that works, and you can use it if you want.
First, one important thing (in my opinion) is to make use of recursive functions, since you're basically going recursively through your path and appending to the global tree.
interface FileInfo {
id: string;
path: string;
}
let files: FileInfo[] = [
{id: '1', path: '/root/library/Folder 1/Document.docx'},
{id: '2', path: '/root/library/Folder 1/Document 2.docx'},
{id: '3', path: '/root/library/Folder 2/Document 3.docx'},
{id: '4', path: '/root/library/Document 4.docx'}
];
interface TreeView{
id: string;
name: string;
children?: TreeView[];
}
// The main function we call, to convert a list of fileInfo to the tree
function convertPathToTreeView(fileInfos: FileInfo[]): TreeView {
// We assume the tree starts at root, it would become very complicated otherwise because we would have no way of knowing the current depth of a path.
let tree: TreeView = {id: 'root_uuid_here', name: 'root', children: []}
// We go over each fileInfo and update the tree with the data contained in it.
fileInfos.forEach((fileInfo) => {
// We split the current path to get the file names, and we remove the leading / before splitting so we don't get '' as a first file name.
const fileNames = fileInfo.path.replace(/^\//, "").split('/')
tree = convertFolderOrFileToTree(tree, fileNames, fileInfo.id)
})
return tree
}
//This is a pretty classical recursive function, that will either return the whole tree, or append a new child to the tree by calling itself on a child of the tree.
function convertFolderOrFileToTree(currentTree: TreeView, fileNames: string[], fileId: string): TreeView {
// This is the base case: when no more files to parse, return.
if (!fileNames.length) {
return currentTree;
}
// If the current tree name is the same as the current file name, it means that the next file in fileNames will be a child of the currentTree. (for example, if currentTree.name === root and fileNames[0] === root, we know that we are at the root, and we can consider that the next fileName will be a child of the current tree, library in this case)
if (currentTree.name === fileNames[0]) {
return convertFolderOrFileToTree(currentTree, fileNames.slice(1), fileId)
} else {
// We check if current fileName is already a child of the tree.
const child = currentTree.children.find(t => t.name === fileNames[0]);
if (child) {
// If it is, we will parse the next fileName as the child of current fileName.
return {...currentTree, children: [...currentTree.children.filter(tree => tree.name !== child.name), convertFolderOrFileToTree(child, fileNames.slice(1),fileId)]}
} else if (fileNames.length > 1) {
// If the fileName is not registered as a child in the tree and it's not the last fileName (meaning it's a folder), we add it and parse the other fileNames recursively
const newTree: TreeView = {id: `${fileNames[0]}_uuid_here`, name: fileNames[0], children: []}
return {...currentTree, children: [...currentTree.children, convertFolderOrFileToTree(newTree, fileNames.slice(1), fileId)]}
} else {
// If the fileName is not registered as a child in the tree and it is the last fileName, meaning it's a file, we just add it to the children and return the tree.
const newTree: TreeView = {id: fileId, name: fileNames[0]}
return {...currentTree, children: [...currentTree.children, newTree]}
}
}
}
console.log(JSON.stringify(convertPathToTreeView(files), null, 4))
Don't hesitate to ask if something is unclear, recursive functions can be a bit abstract.
If you want to allow for other roots than the /root folder, you can just specify an imaginary root, that I will call rootOfTheTree, so we can still have one tree, even if there are different roots.
You could also just have a function that returns several tree (for example if we have /root/file1 and /root2/file2), but it seems to me like that's not what you need.
Here is the code, updated, and without any warning in typescript playground (the children property can be undefined and that caused typescript to not allow just using the property without checking)
interface FileInfo {
id: string;
path: string;
}
let files: FileInfo[] = [
{id: '1', path: '/root/library/Folder 1/Document.docx'},
{id: '2', path: '/root/library/Folder 1/Document 2.docx'},
{id: '3', path: '/root/library/Folder 2/Document 3.docx'},
{id: '4', path: '/root/library/Document 4.docx'}
];
interface TreeView{
id: string;
name: string;
children?: TreeView[];
}
// The main function we call, to convert a list of fileInfo to the tree
function convertPathToTreeView(fileInfos: FileInfo[]): TreeView {
// If we want to allow for different roots but still have one tree, we need to set a rootOfTheTree that is not a folder or a file, but just used to mark the beginning of the tree.
let tree: TreeView = {id: 'rootOfTheTree_uuid_here', name: 'rootOfTheTree', children: []}
// We go over each fileInfo and update the tree with the data contained in it.
fileInfos.forEach((fileInfo) => {
// We split the current path to get the file names, and we remove the leading / before splitting so we don't get '' as a first file name.
const fileNames = fileInfo.path.replace(/^\//, "").split('/')
tree = convertFolderOrFileToTree(tree, fileNames, fileInfo.id)
})
return tree
}
//This is a pretty classical recursive function, that will either return the whole tree, or append a new child to the tree by calling itself on a child of the tree.
function convertFolderOrFileToTree(currentTree: TreeView, fileNames: string[], fileId: string): TreeView {
// This is the base case: when no more files to parse, return.
if (!fileNames.length) {
return currentTree;
}
// If the current tree name is the same as the current file name, it means that the next file in fileNames will be a child of the currentTree. (for example, if currentTree.name === root and fileNames[0] === root, we know that we are at the root, and we can consider that the next fileName will be a child of the current tree, library in this case)
if (currentTree.name === fileNames[0]) {
return convertFolderOrFileToTree(currentTree, fileNames.slice(1), fileId)
} else {
// We check if current fileName is already a child of the tree.
const child = currentTree.children?.find(t => t.name === fileNames[0]);
if (child) {
// If it is, we will parse the next fileName as the child of current fileName.
return {...currentTree, children: [...(currentTree.children?.filter(tree => tree.name !== child.name) ?? []), convertFolderOrFileToTree(child, fileNames.slice(1),fileId)]}
} else if (fileNames.length > 1) {
// If the fileName is not registered as a child in the tree and it's not the last fileName (meaning it's a folder), we add it and parse the other fileNames recursively
const newTree: TreeView = {id: `${fileNames[0]}_uuid_here`, name: fileNames[0], children: []}
return {...currentTree, children: [...(currentTree.children ?? []), convertFolderOrFileToTree(newTree, fileNames.slice(1), fileId)]}
} else {
// If the fileName is not registered as a child in the tree and it is the last fileName, meaning it's a file, we just add it to the children and return the tree.
const newTree: TreeView = {id: fileId, name: fileNames[0]}
return {...currentTree, children: [...(currentTree.children ?? []), newTree]}
}
}
}
console.log(JSON.stringify(convertPathToTreeView(files), null, 4))
Accessing nested JavaScript objects and arrays by string path
I just made this based on some similar code I already had, it appears to work:
Object.byString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
for (var i = 0, n = a.length; i < n; ++i) {
var k = a[i];
if (k in o) {
o = o[k];
} else {
return;
}
}
return o;
}
Usage::
Object.byString(someObj, 'part3[0].name');
See a working demo at http://jsfiddle.net/alnitak/hEsys/
EDIT some have noticed that this code will throw an error if passed a string where the left-most indexes don't correspond to a correctly nested entry within the object. This is a valid concern, but IMHO best addressed with a try / catch
block when calling, rather than having this function silently return undefined
for an invalid index.
Get path of value in object from nested array
You can apply recursion:
const categories = [ { name: 'category1', subcategories: [ { name: 'category2', subcategories: [], }, { name: 'category3', subcategories: [ { name: 'category4', subcategories: [], }, ], }, ], }, { name: 'category5', subcategories: [], },];
const getNestedPath=(arr,name)=>{
for(let item of arr){
if(item.name===name) return `/${name}`;
if(item.subcategories) {
const child = getNestedPath(item.subcategories, name);
if(child) return `/${item.name}${child}`
}
}
};
console.log(getNestedPath(categories, 'category4'));
Create nested array having an element path array
You could reduce the path by preserving the last index and take the result object as start value.
Inside of reduce
assign an array to data
, if it does not exists and assign a new object to the data
property with the actual value pf the path array as index.
var result = {}, data = { id: '1', name: 'first' }, path = [3, 1, 2], last = path.pop(), final = path.reduce((o, i) => { o.data = o.data || []; return o.data[i] = o.data[i] || {}; }, result);
final[last] = Object.assign({}, data, final[last] && { data: final[last].data });
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Concentate values in a Nested Array of Object with children in JavaScript
This would be best done with a recursive Function, that iterates through your entire data structure and sets the Path by building it up while traversing your data structure.
The first version creates the path information from scratch, by using the index of each child in the array and building up the index that gets appended to the path string.
Further below i've provided changes to this version, that uses the already existing path information and concatenates the path string as you asked for.
// Recursive Function to iterate through a possible endless nested data structure
// We provide the parameter for the previous index and parentPath to build up the path string
function recursivePath(data, index = "", parentPath = "") {
// We will get an Array with all Items as 'data' which we will loop with forEach
data.forEach((item, i) => {
// We recreate the the index of the item by adding current index of
// this item in the data array to the index structure of the parent items
let itemIndex = index !== "" ? `${index}.${i+1}` : `${i+1}`;
// We do the same for the path, we take the path of the parent
// and add the path information of this item to it.
let itemPath = `${parentPath}path${itemIndex}`;
// We set the path property of this item, which will be returned
// after all items of this data are done.
item.path = itemPath;
// We check if this item has some nested childrens and if it does,
// we will repeat this process for all those childrens
if (item.children && typeof item.children.length) {
// We provide the newly created index on which those childs will build upon
// as the same with the path.
// This can be a bit confusing, but we assume here, that the function will return
//the finished childrens and we save the result to our childrens property.
item.children = recursivePath(item.children, itemIndex, itemPath + "/");
}
});
// Lastly we iterated through all Items and are sure to set the Path for all Items
// and their childrens nested inside and return the entire data array.
return data;
}
// Your Data
const data = [{
Name: "item1",
path: "path1",
children: [{
Name: "item1.1",
path: "path1.1"
},
{
Name: "item1.2",
path: "path1.2",
children: [{
Name: "item1.2.1",
path: "path1.2.1",
children: [{
Name: "item1.2.1.1",
path: "path1.2.1.1"
}]
}, ]
}
]
}];
// We use the recursive function and output the results to the console
console.log(recursivePath(data));
Create nested object from multiple string paths
I suggest implementing a tree insertion function whose arguments are an array of children and a path. It traverses the children according to the given path and inserts new children as necessary, avoiding duplicates:
// Insert path into directory tree structure:function insert(children = [], [head, ...tail]) { let child = children.find(child => child.name === head); if (!child) children.push(child = {name: head, children: []}); if (tail.length > 0) insert(child.children, tail); return children;}
// Example:let paths = [ '/root/library/Folder 1', '/root/library/Folder 2', '/root/library/Folder 1/Document.docx', '/root/library/Folder 1/Document 2.docx', '/root/library/Folder 2/Document 3.docx', '/root/library/Document 4.docx'];
let objectArray = paths .map(path => path.split('/').slice(1)) .reduce((children, path) => insert(children, path), []);
console.log(objectArray);
Recursive function to add path to nested object using parent object values
You could take a closure over the path.
const
add = p => o => {
const
slug_path = p + (p && '.') + o.slug,
fields = (o.fields || []).map(add(slug_path));
return { ...o, slug_path, ...(fields.length ? { fields } : {}) };
},
addNestedObjSlug = array => array.map(add(''));
console.log(addNestedObjSlug([{ name: 'Item 1', slug: 'i1', fields: [{ name: 'Item 1 - 1', slug: 'i1-1' }, { name: 'Item 1 - 2', slug: 'i1-2' }, { name: 'Item 1 - 3', slug: 'i1-3' }] }, { name: 'Item 2', slug: 'i2', fields: [{ name: 'Item 2 - 1', slug: 'i2-1', fields: [{ name: 'Item 2 - 1 - 1', slug: 'i2-1-1' }] }] }]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
How do I create a nested object structure from a path (array of keys)?
This looks like a job for Array#reduce
:
function objectFromPath (path) { var result = {} path.reduce(function (o, k) { return (o[k] = {}) }, result) return result}
var path = ["test1", "test2", "test3", "test4"]
console.log(objectFromPath(path))
.as-console-wrapper { min-height: 100%; }
Related Topics
How to Choose an Authentication Library For Codeigniter
Continue Processing PHP After Sending Http Response
Highlight the Difference Between Two Strings in PHP
Pass a PHP Array to a JavaScript Function
Convert Multidimensional Array into Single Array
Get the First Element of an Array
Detect Browser Language in PHP
PHP - Check If Two Arrays Are Equal
How to Escape Strings in SQL Server Using PHP
Two-Way Encryption: I Need to Store Passwords That Can Be Retrieved
What Is the Advantage of Using Heredoc in PHP
How to [Recursively] Zip a Directory in PHP
"Date(): It Is Not Safe to Rely on the System'S Timezone Settings..."
How to Repair a Serialized String Which Has Been Corrupted by an Incorrect Byte Count Length
Unicode Character in PHP String
How to Get the Cookies from a PHP Curl into a Variable
Parse Error: Syntax Error, Unexpected End of File in My PHP Code