Printing file path with directory indentation.
Input will be
[
"/root/html/file/a.html",
"/root/html/image/a.jpg",
"/root/html/file/b.html",
"/tmp/c.log"
]
Output needs to be like following,
- root
- html
- file
- a.html
- b.html
- image
- a.jpg
- tmp
- c.log
I couldn't find any solution. I guess it will be a recursive call with a tree structure. Any help would be appreciated.
You could map each file path of your input data into a simple tree-map structure and then recursively print this structure. Something like this (not tested and might still need to handle edge cases):
function processData(data) {
const separator = "/";
const tree = {};
data.forEach(element => mapPathToTree(tree, element.split(separator).filter(part => part)));
printTree(tree, 0);
}
function mapPathToTree(tree, pathParts) {
if (pathParts && pathParts.length <= 1) {
tree[pathParts[0]] = {};
return;
}
const currPart = pathParts[0];
let subTree = tree[currPart];
if (!subTree) {
subTree = {};
tree[currPart] = subTree;
}
mapPathToTree(subTree, pathParts.slice(1));
}
function printTree(subTree, level) {
for (const key in subTree) {
console.log(" ".repeat(level).concat("- ").concat(key));
printTree(subTree[key], level + 1);
}
}
processData([
"/root/html/file/a.html",
"/root/html/image/a.jpg",
"/root/html/file/b.html",
"/tmp/c.log"
]);
Related
I don't know what this is possible.
files
a.html / b.hml
c.css / d.css
There are two changes in Html and Css
Each HTML has a changed Css(c.css,d.css).
The path of Css is as follows
<link src="/style/sample/c.css">
If a Css file has been changed, verify that the CSS file has been declared in the changed HTML.
Change the path of the css file.
Create a new file using Gulf's dest.
That's my code here.
async function ds(cd) {
const branch = branchName.replace("/", "-");
let fileHtml = [],
fileCSS = [],
fileImage = [];
// npm package git-status,
// Look for files that are up on git stage.
await gitStatus((err, data) => {
data.map((e) => {
item = e.to.replace("WebContent/resources", "./resources"); // change path
// Check only Resources
if (item.indexOf("./resources/") !== -1) {
if (item.indexOf("html") !== -1) {
fileHtml.push(item); // get Html List T^T
return fileHtml;
}
if (item.indexOf("css") !== -1) {
// css
fileCSS.push(item);
return fileCSS;
}
if (item.indexOf("img") !== -1) {
// img
fileImage.push(item);
return fileImage;
}
}
});
cd();
});
//I think it's possible with JavaScript, but I don't know what to do with Gulp.
fileHtml.forEach((e) => {
let files = fs.readFileSync(__dirname + e, "utf8"); //해당 HTML 파일
let splitFile = files.split(/\r?\n/);
// 각각의 HTML 파일 내부에서
let result = splitFile.forEach((j, i) => {
// 위에서 찾은 css 파일이 있는지 찾아서
fileCSS.forEach((v) => {
if (j.indexOf(v) >= 0) {
let result = j.replace("/resources/", "/" + branch + "/");
console.log(result);
// console.log("페이지는", e);
// console.log("파일은", v);
}
});
});
});
}
This is not for web development. I am using ES3.
How do I get the information from the xml element proof using javascript in this scenario?
My way of looking for the proof element with xml[xmlVariable] doesn't work - it returns nothing. But when you enter xml.ait.pages.proof in the console (while the program is held by breakpoint at the return expression) it returns the "desired info" from the proof element correctly.
I've read up on dot/bracket notation thinking that would be the solution but nope.
What's the correct syntax here?
<root>
<ait>
<pages>
<proof>desired info</proof>
</pages>
</ait>
</root>
var xmlFile = "C:\Users\user\Desktop\info.xml"
var xmlElementPath = "ait.pages.proof"
var info = readXMLVar(xmlElementPath, xmlFile)
function readXMLVar(xmlVariable, xmlFilePath) {
var file = new File(xmlFilePath)
file.open("r")
var content = file.read()
file.close()
var xml = new XML(content)
return xml[xmlVariable]
}
For XML I would probably query using XPath. The code you're using, however, seems to create an object structure from the parsed XML, and you then want to ask for a part of that structure using a path to it, as it were.
You can use square bracket notation as you tried, but you have to do it one property/node-level at a time. JS doesn't parse the dot separated path you provided to walk into the nested structure.
As such, you need something that can break apart the path you want, and recursively walk down the structure node by node.
Here is a basic function that can walk an object structure:
var getNodeFromPath = function (data, path, separator) {
var node_name,
node,
ret;
if (!Array.isArray(path)) {
path = path.split(separator || '.');
}
node_name = path.shift();
node = data[node_name];
if (node === undefined) {
ret = null;
} else {
if (path.length) {
ret = getNodeFromPath(node, path);
} else {
ret = node;
}
}
return ret;
};
You could call it like so:
var proof_element = getNodeFromPath(yourParsedXmlData, 'ait.pages.proof');
Note that the function I gave you has minimal control in it. You'll probably want to add some checking to make it more resistant to arbitrary input data/path problems.
Applied fixes to JAAulde's answer and some slight modifications to fit into my function. Here is my code to get and set XML variables.
!(Object.prototype.toString.call(path) === '[object Array]') is used in place of !Array.isArray(path) because I'm forced to use ES3.
function readXMLFile(xmlFilePath) {
var file = new File(xmlFilePath)
file.open("r")
var content = file.read()
file.close()
return [file, new XML(content)]
}
function getXMLVar(xmlFilePath, nodePath, separator) {
var xml = readXMLFile(xmlFilePath)[1]
// navigate xml to return target node info
var getNodeFromPath = function(data, path, separator) {
var node_name,
node,
ret
if(!(Object.prototype.toString.call(path) === '[object Array]')) {
path = path.split(separator || '.')
}
node_name = path.shift()
node = data[node_name]
if(node === undefined) {
ret = null
} else {
if(path.length) {
ret = getNodeFromPath(node, path, separator)
} else {
ret = node
}
}
return ret
}
return getNodeFromPath(xml, nodePath, separator)
}
function setXMLVar(xmlFilePath, nodePath, separator, value) {
var read = readXMLFile(xmlFilePath)
var file = read[0]
var xml = read[1]
setNodeFromPath = function(data, path, separator, value) {
var node_name,
node
if(!(Object.prototype.toString.call(path) === '[object Array]')) {
path = path.split(separator || '.')
}
node_name = path.shift()
node = data[node_name]
if(path.length > 1) {
setNodeFromPath(node, path, separator, value)
} else {
node[path[0]] = value
}
}
setNodeFromPath(xml, nodePath, separator, value)
file.open("w")
file.write(xml)
file.close()
}
Reading a bunch of translation files in a file directory and assigning the data to a global object so that I can pull a translation using i18nContent.messages.en.9999
File tree is like this
locales
messages
en.json => {"9999": "Unexpected Error", "0": "Success"}
de.json => {"9999": "German Error", "0": "German Success"}
emails
en.json => {"signupEmail": "Thanks for signing up", "passwordEmail": "Password changed"}
de.json => {"signupEmail": "German signing up", "passwordEmail": "German Password changed"}
I am able to get all of the "names" for each of the nested objects. However, I am unable to assemble the total object during the directory walk.
ATTEMPT 1
global.i18nContent = {};
walkDir(dir, function (filePath, dir) {
if (filePath.substr(-5) === ".json") {
let directory = dir.split(/[\s\/]+/);
directory = directory[directory.length - 1];
let lang = filePath.split(/[\s\/]+/);
lang = lang[lang.length - 1].substr(-7, 2);
//this substr fix is to make this work on Macs
let rem = __dirname.toString().substr(0, __dirname.toString().length - 3);
let langFolder = {};
langFolder[lang] = require(filePath.replace(rem, '../'));
Object.assign(i18nContent[directory], langFolder);
}
});
ATTEMPT 2
Removed
let langFolder = {};
langFolder[lang] = require(filePath.replace(rem, '../'));
Object.assign(i18nContent[directory], langFolder);
and just tried i18nContent[directory][lang] = require(filePath.replace(rem, '../')) or i18nContent[directory[lang]] = require(filePath.replace(rem, '../'))
console output is showing ['undefined']
Should be nested to reference like i18nContent.messages.en.9999
I assume that you are using walkdir, right? Then I would just do it like this:
global.i18nContent = {};
const emitter = walkDir(yourDir);
emitter.on('file', filename => {
const parts = filename.match(/^.*\/locales\/(\w*)\/(\w*)\.json$/);
if (!parts) return;
const [, folderName, lang] = parts;
if (!global.i18nContent[folderName]) global.i18nContent[folderName] = {};
global.i18nContent[folderName][lang] = require(filename);
});
I have a file , in javascript , that find all the directories that match the parameter.
And i got this error:
my code:
function getUserHome() {
return process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
}
var home_path=getUserHome();
var findit = require('findit'),
path = require('path'),
finder = findit(path.resolve(home_path));
var myArgs = process.argv.slice(2)[0];
var filter1 = new RegExp(myArgs);
//This listens for directories found
finder.on('directory', function (dir) {
var directories = dir.split('\\');
var last= directories[directories.length-1].toLowerCase();
if(filter1.test(last)){
console.log('Directory: ' + dir );
}
});
(My code is a mess, i will clean it later)
How to fix that?
Why you didn't user the fs from Node, and look for dirs recursively? I think the error should be on the findit module...
That a look on https://nodejs.org/api/fs.html#fs_fs_readdir_path_callback or try instead the https://www.npmjs.com/package/recursive-readdir that also does it. I think that the things you use from the module findit, will be available there (like ways to ignore files)...
EDIT1: Example using recursive-readdir:
var recursive = require('recursive-readdir');
var filter1 = new RegExp(myArgs);
function ignoreFunc(file, stats) {
return !(stats.isDirectory() && filter1.test(path.basename(file)));
}
recursive('directory', [ignoreFunc] ,function (err, files) {
// Files is an array of filename (only the ones that matched the condition)
console.log(files);
});
#Moran, can you add a console.log directly in the callback of you "directory" event ?
finder.on('directory', function (dir) {
// Here
console.log(dir);
var directories = dir.split('\\');
var last= directories[directories.length-1].toLowerCase();
if(filter1.test(last)){
console.log('Directory: ' + dir );
}
});
To see what directory is problematic ? Then compare the rights applied on this folder and a directory that work, as "comverse" for example. It would help to find your error
I'm trying to create a tree view from file paths, which can be added and removed dinamically, for instance:
A/B/C/D/file1.txt
A/B/D/E/file2.txt
A/B/D/G/file3.txt
A/B/D/G/file4.txt
My tree, however, has a requirement that paths with no child items (files) should be collapsed in one node. For the paths above it would yield:
A/B
|---C/D
file1.txt
|---D
|---E
| file2.txt
|---G
file3.txt
file4.txt
Any thoughts? Creating the tree is easy, but I can't get past that extra condition... I assume I'd have to use some kind of recursion as I go adding items and breaking the paths as we find that a certain path has more children (and then doing the same recursively?). Should I use some kind of trie? Would it work when the same path can have multiple files?... Thanks!
Let's begin by a simple solution to print the tree as it actually is:
function browseTree(node)
{
// ...print node...
// Visit recursively the children nodes:
for (var child: node.children)
{
browseTree(child);
}
}
Now, let's modify it to "shorten" the single-folder paths:
function browseTree(node)
{
// Before printing, accumulate as many straight folders as possible:
var nodeName=node.name
while (hasJustOneFolder(node))
{
// This loop steps deeper in the tree:
node=node.children[0]
nodeName+="/"+node.name;
}
// ...print node...
// Last, visit recursively the non-unique children nodes:
for (var child: node.children)
{
browseTree(child);
}
}
function hasJustOneFolder(node)
{
return node.children.length==1 && node.children[0].isFolder();
}
Given your requirements, it seems like adding a new file to, say, C, wouldn't imply recursive operations.
If you add file5.txt to folder C, you have to transform C/D in a node C having 2 children: file5.txt and a new node called D. D will have the same children as the old node C/D. You can then erase node C/D.
However, this will not affect node A/B, because folder A will still have only one folder (B) as a child. Thus you could solve the problem making only local changes.
I create a sample code with custom tree node format with Map and the print function is a generator function to get line by line the path of the tree.
// Node
class NodePath {
constructor(e) {
this.isFolder = e.isFolder;
this.name = e.name;
this.childs = new Map();
}
}
// Make path tree
function makePathsTree(paths) {
const treeRoot = new NodePath({isFolder: true, name: "*"});
for (const path of paths) {
// Set current post as root
let curPos = treeRoot;
// For each part
const parts = path.split("/");
while (parts.length) {
// Get cur
const curPart = parts.shift();
// Get child node, create if not exists
let childNode = curPos.childs.get(curPart);
if (!childNode) {
childNode = new NodePath({
isFolder: !!parts.length,
name: curPart,
});
curPos.childs.set(curPart, childNode)
}
// Update cur post to child node
curPos = childNode;
}
}
// Return tree
return treeRoot;
}
// Generator function prevent huge large file system strings
function *printPathsTree(node, offset = 0, prev = "") {
// Offset str
const offsetStr = " ".repeat(offset);
// Is folder
if (!node.isFolder) {
yield `${offsetStr}${prev}${node.name}`;
return;
}
// If one child and is folder, merge paths
if (node.childs.size === 1) {
const child = node.childs.values().next().value;
if (child.isFolder === true) {
for (const childData of printPathsTree(child, offset, `${prev}${node.name}/`)) {
yield childData;
}
return;
}
}
// Print node name
yield `${offsetStr}${prev}${node.name}`;
// For each child, print data inside
for (const child of node.childs.values()) {
for (const childData of printPathsTree(child, offset + prev.length, "|---")) {
yield childData;
}
}
}
// == CODE ==
console.log("WITH ROOT:");
const tree = makePathsTree([
"A/B/C/D/file1.txt",
"A/B/C/D/file2.txt",
"A/B/D/E/file2.txt",
"A/B/D/G/file3.txt",
"A/B/D/G/file4.txt",
]);
// Print tree step by step
for(const nodePath of printPathsTree(tree)) {
console.log(nodePath);
}
// Print with A as root
console.log("\nA AS ROOT:");
for(const nodePath of printPathsTree(tree.childs.values().next().value)) {
// for(const nodePath of printPathsTree(tree.childs.get("A"))) { // same
console.log(nodePath);
}
Output:
WITH ROOT:
*/A/B
|---C/D
|---file1.txt
|---file2.txt
|---D
|---E
|---file2.txt
|---G
|---file3.txt
|---file4.txt
A AS ROOT:
A/B
|---C/D
|---file1.txt
|---file2.txt
|---D
|---E
|---file2.txt
|---G
|---file3.txt
|---file4.txt
You can build the tree by recursively grouping on the leading path name, and then merging parent-child names if a parent only has one child:
var paths = ["A/B/C/D/file1.txt", "A/B/C/D/file2.txt", "A/B/D/E/file2.txt", "A/B/D/G/file3.txt", "A/B/D/G/file4.txt"]
function merge_paths(paths){
var d = {};
var new_d = {}
for (var [a, ...b] of paths){
d[a] = (a in d) ? [...d[a], b] : [b]
}
for (var i of Object.keys(d)){
if (d[i].every(x => x.length === 1)){
new_d[i] = d[i].map(x => x[0]);
}
else{
var k = merge_paths(d[i])
if (Object.keys(k).length > 1){
new_d[i] = k
}
else{
new_d[`${i}/${Object.keys(k)[0]}`] = k[Object.keys(k)[0]]
}
}
}
return new_d;
}
var result = merge_paths(paths.map(x => x.split('/')))
console.log(result)