JavaScript - Dynamically generate a file path from an object - javascript

I've looked for hours and still I couldn't find anything about it so I'm asking here. I have a small PHP script which generates a JSON tree of files and folders, and then fetches it to client side as a JavaScript variable. The outcome usually looks like this:
{
"folder_name" : {
"another_folder": {
"third_folder": {
0: "some_file.txt"
1: "another_file.png"
2: "third_file.pdf"
}
}
}
}
What I'm wanted to achive would look something like this:
generatePath("some_file.txt")
Which would return:
"folder_name/another_folder/third_folder/some_file.txt"
So my question is how can I create a path to any of those files just from an object in JavaScript? Is there even a way to do anything like this?
[Edit]: Sadly I don't have any code to show anymore...

So assuming the input is a real JSON and it will parse into JS object here is some approach:
const data = {
"folder_name" : {
"another_folder": {
"third_folder": {
"0": "some_file.txt",
"1": "another_file.png",
"2": "third_file.pdf"
}
}
},
"folder_name2" : {
"0": "up_file.txt",
"another_folder2": {
"third_folder2": ["some_file2.txt", "another_file2.png", "third_file2.pdf"]
}
}
}
function generatePath(value, currentPath = '', currentObject = data) {
for (const property in currentObject) {
if (currentObject[property] === value) {
return `${currentPath}/${value}`;
}
if (typeof currentObject[property] === 'object') {
const result = generatePath(value, `${currentPath && currentPath + '/'}${property}`, currentObject[property]);
if (result) {
return result;
}
}
}
}
document.write(
generatePath("some_file.txt"),
'<br>',
generatePath("some_file2.txt"),
'<br>',
generatePath("up_file.txt"),
'<br>',
generatePath("no_file.txt")
);

You can create recursive function for this using for...in loop that will store previous path elements in one array.
const data = {
"folder_name": {
"another_folder": {
"third_folder": {
0: "some_file.txt",
1: "another_file.png",
2: "third_file.pdf"
}
}
}
}
function generatePath(data, file) {
let result
(function getPath(obj, file, prev = []) {
for (let i in obj) {
if (typeof obj[i] == 'object') {
getPath(obj[i], file, prev.concat(i).slice())
}
if (file == obj[i]) {
result = prev.concat(obj[i]).join('/')
}
}
})(data, file)
return result;
}
console.log(generatePath(data, "third_file.pdf"))
console.log(generatePath(data, "some_file.txt"))

Related

google apps script traverse object

I've searched and searched but cannot find a better way to search through a JSON object and return a nested object that corresponds to a specific key.
I've found examples that work in javascript but when I try to use that code in Google Apps Script I find that some of the function/modules are not supported.
deeply The script below works where objRet is a global variable but I wondered if there was a better way to do it?
var objRet;
function blahblah() {
traverse(api_info, "EarningsRates");
// use this.objRet to process code
}
function traverse(json, keyData) {
var keyData = keyData;
var json = json;
if (Array.isArray(json)) {
json.forEach(traverse);
} else if (typeof json === 'object') {
Object.keys(json).forEach(function(key) {
if (key === keyData) {
this.retObj = json[key];
} else {
traverse(json[key], keyData);
}
});
}
}
I found this and would love to get it working but no luck with Google Apps Script
This function implements DFS: (depth first search)
function findDFS(objects, id) {
for (let o of objects || []) {
if (o.uuid == id) return o
const o_ = findDFS(o.children, id)
if (o_) return o_
}
}
And BFS:(breadth first search)
function findBFS(objects, id) {
const queue = [...objects]
while (queue.length) {
const o = queue.shift()
if (o.uuid == id) return o
queue.push(...(o.children || []))
}
}

Read nested json file in javascript using node js in simple Key-> Value Pair format [duplicate]

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 6 years ago.
I have been working with complex,nested JSON file as given below:
Update:: Nested JSON file snippet (example)
{
"sample": {
"someitem": {
"thesearecool": [
{
"neat": "wow"
},
{
"neat": "tubular"
}
]
},
"coolcolors": [
{
"color":"red",
"hex": "ff0000"
},
{
"color":"blue",
"hex":"0000ff"
}
]
}
}
I want to traverse through each and every value in this JSON file.
I have tried many npm nodejs packages to make nested JSON into plain, readable JSON format.(NPM Packages-> flattenr, flat, etc.).
Kindly someone help me to resolve this issue. Please give some better solutions with examples.
Yeah, that's a nice problem of recursion.
So, the general idea is a function with a for-loop.
2 things can happen: either it's a value, then you print it. Or it's a object, then you put that object through the same function.
<div id="log"></div>
<script>
var data = {
"sample": {
"someitem": {
"thesearecool": [
{
"neat": "wow"
},
{
"neat": "tubular"
}
]
},
"coolcolors": [
{
"color":"red",
"hex": "ff0000"
},
{
"color":"blue",
"hex":"0000ff"
}
]
}
};
function readAllJson(data, level) {
var resultString = '';
for(var i in data) {
var type = typeof data[i];
switch(type) {
case 'object':
resultString += indent(level) + i +':<br/>'+ readAllJson(data[i], level + 1); // recursion
break;
default:
resultString += indent(level) + i +': '+ data[i] + '<br/>';
break;
}
}
return resultString;
}
function indent(level) {
var result = '';
for(var i=0; i<level; i++) {
result += ' '; // HTML space character
}
return result;
}
window.onload = function() {
var log = document.getElementById('log');
var result = readAllJson(data, 0);
log.innerHTML = result;
}
</script>

recursively generate filepaths from object properties

I am using node.js and as a side project i am creating a module that reads a .json file ,parse it then create directory structure based on object properties & object values.
Object properties(keys) would be the path to itself/to files & object values would be the list of files for that path
i have tried to recurse downwards through the object but i dont know how i extract the path from the inner-most object of each object
Also object would be dynamic as would be created by the user.
var path = 'c:/templates/<angular-app>';
var template = {
//outline of 'angular-app'
src:{
jade:['main.jade'],
scripts:{
modules:{
render:['index.js'],
winodws:['index.js'],
header:['header.js' ,'controller.js'],
SCSS:['index.scss' ,'setup.scss'],
}
}
},
compiled:['angular.js','angular-material.js' ,'fallback.js'],
built:{
frontEnd:[],//if the array is empty then create the path anyways
backEnd:[],
assets:{
fontAwesome:['font-awesome.css'],
img:[],
svg:[]
}
}
}
//desired result...
let out = [
'c:/template name/src/jade/main.jade',
'c:/template name/src/scripts/index.js',
'c:/template name/src/scripts/modules/render/index.js',
'c:/template name/compiled/angular.js',
'c:/template name/compiled/angular-material.js',
'c:/template name/compiled/fallback.js',
'c:/template name/built/frontEnd/',
'c:/template name/built/backEnd/',
//...ect...
];
Here's an example on how you can write this recursively:
var path = 'c:/templates';
var template = {
//outline of 'angular-app'
src: {
jade: ['main.jade'],
scripts: {
modules: {
render: ['index.js'],
winodws: ['index.js'],
header: ['header.js', 'controller.js'],
SCSS: ['index.scss', 'setup.scss'],
}
}
},
compiled: ['angular.js', 'angular-material.js', 'fallback.js'],
built: {
frontEnd: [], //if the array is empty then create the path anyways
backEnd: [],
assets: {
fontAwesome: ['font-awesome.css'],
img: [],
svg: []
}
}
}
function recurse(item, path, result) {
//create default output if not passed-in
result = result || [];
//item is an object, iterate its properties
for (let key in item) {
let value = item[key];
let newPath = path + "/" + key;
if (typeof value === "string") {
//if the property is a string, just append to the result
result.push(newPath + "/" + value);
} else if (Array.isArray(value)) {
//if an array
if (value.length === 0) {
//just the directory name
result.push(newPath + "/");
} else {
//itearate all files
value.forEach(function(arrayItem) {
result.push(newPath + "/" + arrayItem);
});
}
} else {
//this is an object, recursively build results
recurse(value, newPath, result);
}
}
return result;
}
var output = recurse(template, path);
console.log(output);
My solution for this problem would be as follows;
function getPaths(o, root = "", result = []) {
var ok = Object.keys(o);
return ok.reduce((a,k) => { var p = root + k + "/";
typeof o[k] == "object" && o[k] !== null &&
Array.isArray(o[k]) ? o[k].length ? o[k].forEach(f => a.push(p+=f))
: a.push(p)
: getPaths(o[k],p,a);
return a;
},result);
}
var path = 'c:/templates/',
template = {
//outline of 'angular-app'
src:{
jade:['main.jade'],
scripts:{
modules:{
render:['index.js'],
winodws:['index.js'],
header:['header.js' ,'controller.js'],
SCSS:['index.scss' ,'setup.scss'],
}
}
},
compiled:['angular.js','angular-material.js' ,'fallback.js'],
built:{
frontEnd:[],//if the array is empty then create the path anyways
backEnd:[],
assets:{
fontAwesome:['font-awesome.css'],
img:[],
svg:[]
}
}
},
paths = getPaths(template,path);
console.log(paths);
It's just one simple function called getPaths Actually it has a pretty basic recursive run. If your object is well structured (do not include any properties other than objects and arrays and no null values) you may even drop the typeof o[k] == "object" && o[k] !== null && line too. Sorry for my unorthodox indenting style but this is how i find to deal with the code more easy when doing ternaries, logical shortcuts and array methods with ES6 arrow callbacks.

If statement not returning as supposed

I might seem really dumb but this piece of code is really frustating me.
if(fs.exist(parametters[0])){
fs.remove(parametters[0]);
return "removed";
}else{
return "doesn't exist"
}
The thing is, the fs.remove() is actually called but the function is returning "doesnt't exist", Am I missing something?
I'm not using nodejs, this is from one library i made, is asynchronously.
It's not modifying the parametters but it does change the condition, might be that?
Well I'm posting my fs object although I don't think this will change anything.
fs = {
load: function() {
if (localStorage[0] == undefined || localStorage[0] == "undefined" || localStorage[0] == "") {
localStorage[0] = JSON.stringify(fs.files);
} else {
fs.files = JSON.parse(localStorage[0]);
}
},
save: function() {
localStorage[0] = JSON.stringify(fs.files);
},
files: [],
newFile: function(name, content, overwrite) {
if (overwrite == undefined)
overwrite = true;
if (fs.exist(name) && overwrite) {
fs.find(name).content = content;
fs.save();
}
if (!(fs.exist(name))) {
fs.files.push({
name: name,
content: content
});
fs.save();
}
},
exist: function(fileName) {
for (var i = 0; i < fs.files.length; i++) {
if (fs.files[i].name == fileName)
return true;
}
return false;
},
find: function(fileName) {
for (var i = 0; i < fs.files.length; i++) {
if (fs.files[i].name == fileName)
return fs.files[i];
}
return false;
},
format: function() {
fs.files = [];
localStorage[0] = undefined;
},
write: function(name, content, overwrite) {
if (overwrite == undefined)
overwrite = true;
if (fs.exist(name) && overwrite) {
fs.find(name).content = content;
fs.save();
}
if (!(fs.exist(name))) {
fs.files.push({
name: name,
content: content
});
fs.save();
}
},
remove: function(file) {
var arrToreturn = [];
for (var i = 0; i < fs.files.length; i++) {
if (fs.files[i].name != file)
arrToreturn.push(fs.files[i]);
}
fs.files = arrToreturn;
fs.save();
return arrToreturn;
}
}
Resolved -
After a few days of inspecting the code I found the bug where the function was called twice, the amount of code was really huge so it took me a while.
You need to add a semi-colon to return "doesn't exist", it should read return "doesn't exist";
If this is an Object, still it works.
We can assume this to be an File object ARRAY, indexOf still works to find if the item exists.
Please have a look upon below example:
var fruits = ["Banana", "Orange", "Apple", "Mango"];
var a = fruits.indexOf("Apple");
Result is 2 in case Apple is found
Result is -1 in case Apple is not found
You can have some more options at this link: http://www.w3schools.com/jsref/jsref_indexof_array.asp
Thanks
Use This Code For Solving This Problam. thanks
var fs = require('fs-extra')
fs.remove('/tmp/myfile', function (err) {
if (err) return console.error(err)
console.log('success!')
})
fs.removeSync('/home/jprichardson') //I just deleted my entire HOME directory.
You can try javascript indexOf function to check if the value really exists, BEFORE REMOVE Operation.
Example below:
var str = "Hello world, welcome to the universe.";
var n = str.indexOf("welcome");
=> Gives 13 if found
if we search for "welcome1" -> will give -1

Nicer way to get nested object attributes

Often in a response from a remote API call, I receive nested objects:
var response = {
data : {
users : [
{
name : 'Mr. White'
}
]
}
}
I want to check whether the first user's name is 'Mr. White', and would naturally want to write something like.
var existed = response.data.users[0].name === 'Mr. White'
However I cannot be sure if all the objects are present, so to avoid exceptions instead I end up writing:
var existed = response && response.data && response.data.users && response.data.users[0].name === 'Mr. White'
Is there a nicer way to do this? Another ugly option that comes to mind is:
var existed = false;
try {
var existed = response.data.users[0].name === 'Mr. White';
} catch(e) { }
In addition to vanilla javascript, I usually have underscore.js and jquery available too.
Edit:
Oops, noticed I asked a dupe of javascript test for existence of nested object key.
An interesting option based on those answers is:
var existed = (((response || {}).data || {}).users || [{}])[0].name === 'Mr. White';
You could hide this naughty try/catch block inside a function like this one :
function resolve(root, path){
try {
return (new Function(
'root', 'return root.' + path + ';'
))(root);
} catch (e) {}
}
var tree = { level1: [{ key: 'value' }] };
resolve(tree, 'level1[0].key'); // "value"
resolve(tree, 'level1[1].key'); // undefined
More on this : https://stackoverflow.com/a/18381564/1636522
I would use the try catch approach but wrap it in a function to hide the ugliness.
Instead of a try/catch, this should be done via checking whether each level in the object is defined or not.
go for
if(typeof(response)!="undefined"
&& typeof(response.data)!="undefined"
&& typeof(response.data.users)!="undefined"
&& typeof(response.data.users[0])!="undefined"
&& typeof(response.data.users[0].name)!="undefined"
) {
//executes only if response.data.users[0].name is existing
}
Here is a function which I used in one of my projects http://jsfiddle.net/JBBAJ/
var object = {
data: {
users: [
{
firstName: "White"
},
{
firstName: "Black"
}
]
}
}
var read = function(path, obj) {
var path = path.split(".");
var item = path.shift();
if(item.indexOf("]") == item.length-1) {
// array
item = item.split("[");
var arrayName = item.shift();
var arrayIndex = parseInt(item.shift().replace("]", ""));
var arr = obj[arrayName || ""];
if(arr && arr[arrayIndex]) {
return read(path.join("."), arr[arrayIndex]);
} else {
return null;
}
} else {
// object
if(obj[item]) {
if(path.length === 0) {
return obj[item];
} else {
return read(path.join("."), obj[item]);
}
} else {
return null;
}
}
}
console.log(read("data.users[0].firstName", object)); // White
console.log(read("data.users[1].firstName", object)); // Black
console.log(read("data.test.users[0]", object)); // null
The idea is to pass your path as a string along with your object. The idea was to prevent the throwing of an exception and receive just null as result of the path is wrong. The good thing is that the function works with every path and you don't need to write long if statements.

Categories

Resources