I am working on a MQTT handler for which I want to emit an event for each parent directory where there is a event listener. For example:
If there are the following MQTT paths available, where there are subscriptors –there are event listeners for these paths–
test
replyer/request
test/replyer/request
And someone publishes on topic test/replyer/request/#issuer, there should be 2 events emmited: test, test/replyer/request.
Given than any path is possible and there is no list of available valid events, we must check only if a path is a parent of another. Can we do this with regex? If so, how would it look like? Is there a simpler/more efficient solution?
Let Node itself do the work.
const path = require('path');
const relative = path.relative(parent, dir);
return relative && !relative.startsWith('..') && !path.isAbsolute(relative);
It does normalisation for you as well.
const path = require('path');
const tests = [
['/foo', '/foo'],
['/foo', '/bar'],
['/foo', '/foobar'],
['/foo', '/foo/bar'],
['/foo', '/foo/../bar'],
['/foo', '/foo/./bar'],
['/bar/../foo', '/foo/bar'],
['/foo', './bar'],
['C:\\Foo', 'C:\\Foo\\Bar'],
['C:\\Foo', 'C:\\Bar'],
['C:\\Foo', 'D:\\Foo\\Bar'],
];
tests.forEach(([parent, dir]) => {
const relative = path.relative(parent, dir);
const isSubdir = relative && !relative.startsWith('..') && !path.isAbsolute(relative);
console.log(`[${parent}, ${dir}] => ${isSubdir} (${relative})`);
});
Works on Windows across drives too.
[/foo, /foo] => false ()
[/foo, /bar] => false (..\bar)
[/foo, /foobar] => false (..\foobar)
[/foo, /foo/bar] => true (bar)
[/foo, /foo/../bar] => false (..\bar)
[/foo, /foo/./bar] => true (bar)
[/bar/../foo, /foo/bar] => true (bar)
[/foo, ./bar] => false (..\Users\kozhevnikov\Desktop\bar)
[C:\Foo, C:\Foo\Bar] => true (Bar)
[C:\Foo, C:\Bar] => false (..\Bar)
[C:\Foo, D:\Foo\Bar] => false (D:\Foo\Bar)
2021 Answer
Use #Ilya's solution.
2017 Answer
In ES6.
const isChildOf = (child, parent) => {
if (child === parent) return false
let parentTokens = parent.split('/').filter(i => i.length)
let childTokens = child.split('/').filter(i => i.length)
return parentTokens.every((t, i) => childTokens[i] === t)
}
If you're working in node.js and you want to make it cross-platform, include the path module and replace split('/') with split(path.sep).
How it works:
So, you want to find out if a directory (like home/etc/subdirectory) is a subdirectory of another directory (like home/etc).
It takes both the hypothesised child and parent paths and convert them into arrays using split:
['home', 'etc', 'subdirectory'], ['home', 'etc']
It then iterates through all of the tokens in the parent array and checks them one-by-one against their relative position in the child array using ES6's .every().
If everything in parent matches up to everything in child, knowing that we've ruled out they are exactly the same directory (using child !== parent), we will have our answer.
For those who care about performances, which seem to pass unnoticed to people who have already answered, checking whether the sub path starts with its parent path should be enough.
const path = require('path');
function isSubPathOf(subPath, parentPath) {
parentPath = normalize(parentPath);
if (subPath.length <= parentPath.length)
return false;
function normalize(p) {
p = path.normalize(p);
if (!p.endsWith(path.sep))
p += path.sep;
return p;
}
subPath = normalize(subPath);
return subPath.startsWith(parentPath);
}
console.log(isSubPathOf('/a/b/c/d/e', '/a/b/c'));
console.log(isSubPathOf('/a/b/c/de', '/a/b/c'));
console.log(isSubPathOf('/a/b/c', '/a/y/c'));
console.log(isSubPathOf('/a/y/c/k', '/a/y/c'));
This is a really old question, but I came up with a dead simple solution for this using Node's built–in path.relative: if the child is inside the parent, the relative path from the former to the latter will always start with '..'.
import { relative } from 'path';
function isSubDirectory(parent, child) {
return relative(child, parent).startsWith('..');
}
There are a couple of things going on here that are needed to prevent failure:
Should we attempt to resolve filesystem paths? (I think so)
Checking if one directory contains another should work with symlinks
I came up with a solution that attempts to resolve filesystem paths as much as possible while allowing paths which may or may not exist:
Split the path on the OS's path separator
Resolve as many of those path components in the filesystem as possible
Append remaining components that couldn't be resolved
If the relative path between parent and child doesn't start with .. + path.sep and isn't .. then the parent path contains the child path
This all works, assuming that any nonexistent path components would be created using only directories and files (no symlinks). For example say your script needs to write only to whitelisted paths and you're accepting untrusted (user-supplied) filenames. You could create subdirectories using something like PHP's mkdir with $recursive = true to create the directory structure in one step, similar to this example.
Here is the code (not runnable until Stack Overflow supports Node.js), the important functions are resolveFileSystemPath() and pathContains():
const kWin32 = false;
const fs = require('fs');
const path = kWin32 ? require('path').win32 : require('path');
////////// functions //////////
// resolves (possibly nonexistent) path in filesystem, assuming that any missing components would be files or directories (not symlinks)
function resolveFileSystemPath(thePath) {
let remainders = [];
for (
let parts = path.normalize(thePath).split(path.sep); // handle any combination of "/" or "\" path separators
parts.length > 0;
remainders.unshift(parts.pop())
) {
try {
thePath =
fs.realpathSync(parts.join('/')) + // fs expects "/" for cross-platform compatibility
(remainders.length ? path.sep + remainders.join(path.sep) : ''); // if all attempts fail, then path remains unchanged
break;
} catch (e) {}
}
return path.normalize(thePath);
}
// returns true if parentPath contains childPath, assuming that any missing components would be files or directories (not symlinks)
function pathContains(parentPath, childPath, resolveFileSystemPaths = true) {
if (resolveFileSystemPaths) {
parentPath = resolveFileSystemPath(parentPath);
childPath = resolveFileSystemPath(childPath);
}
const relativePath = path.relative(parentPath, childPath);
return !relativePath.startsWith('..' + path.sep) && relativePath != '..';
}
////////// file/directory/symlink creation //////////
console.log('directory contents:');
console.log();
try {
fs.mkdirSync('parent');
} catch (e) {} // suppress error if already exists
fs.writeFileSync('parent/child.txt', 'Hello, world!');
try {
fs.mkdirSync('outside');
} catch (e) {} // suppress error if already exists
try {
fs.symlinkSync(path.relative('parent', 'outside'), 'parent/child-symlink');
} catch (e) {} // suppress error if already exists
fs.readdirSync('.').forEach(file => {
const stat = fs.lstatSync(file);
console.log(
stat.isFile()
? 'file'
: stat.isDirectory() ? 'dir ' : stat.isSymbolicLink() ? 'link' : ' ',
file
);
});
fs.readdirSync('parent').forEach(file => {
file = 'parent/' + file;
const stat = fs.lstatSync(file);
console.log(
stat.isFile()
? 'file'
: stat.isDirectory() ? 'dir ' : stat.isSymbolicLink() ? 'link' : ' ',
file
);
});
////////// tests //////////
console.log();
console.log(
"path.resolve('parent/child.txt'): ",
path.resolve('parent/child.txt')
);
console.log(
"fs.realpathSync('parent/child.txt'): ",
fs.realpathSync('parent/child.txt')
);
console.log(
"path.resolve('parent/child-symlink'): ",
path.resolve('parent/child-symlink')
);
console.log(
"fs.realpathSync('parent/child-symlink'):",
fs.realpathSync('parent/child-symlink')
);
console.log();
console.log(
'parent contains .: ',
pathContains('parent', '.', true)
);
console.log(
'parent contains ..: ',
pathContains('parent', '..', true)
);
console.log(
'parent contains parent: ',
pathContains('parent', 'parent', true)
);
console.log(
'parent contains parent/.: ',
pathContains('parent', 'parent/.', true)
);
console.log(
'parent contains parent/..: ',
pathContains('parent', 'parent/..', true)
);
console.log(
'parent contains parent/child.txt (unresolved): ',
pathContains('parent', 'parent/child.txt', false)
);
console.log(
'parent contains parent/child.txt (resolved): ',
pathContains('parent', 'parent/child.txt', true)
);
console.log(
'parent contains parent/child-symlink (unresolved):',
pathContains('parent', 'parent/child-symlink', false)
);
console.log(
'parent contains parent/child-symlink (resolved): ',
pathContains('parent', 'parent/child-symlink', true)
);
Output:
directory contents:
file .bash_logout
file .bashrc
file .profile
file config.json
dir node_modules
dir outside
dir parent
link parent/child-symlink
file parent/child.txt
path.resolve('parent/child.txt'): /home/runner/parent/child.txt
fs.realpathSync('parent/child.txt'): /home/runner/parent/child.txt
path.resolve('parent/child-symlink'): /home/runner/parent/child-symlink
fs.realpathSync('parent/child-symlink'): /home/runner/outside
parent contains .: false
parent contains ..: false
parent contains parent: true
parent contains parent/.: true
parent contains parent/..: false
parent contains parent/child.txt (unresolved): true
parent contains parent/child.txt (resolved): true
parent contains parent/child-symlink (unresolved): true
parent contains parent/child-symlink (resolved): false
Live example: https://repl.it/repls/LawngreenWorriedGreyware
The last line of the output is the important one, showing how resolved filesystem paths lead to the correct result (unlike the unresolved result above it).
Limiting filesystem reads/writes to certain directories is so important for security that I hope Node.js incorporates this functionality into their builtins. I haven't tested this on a native Windows box so please let me know if the kWin32 flag is working. I'll try to curate this answer as time allows.
Based on & improved Dom Vinyard's code:
const path = require('path');
function isAncestorDir(papa, child) {
const papaDirs = papa.split(path.sep).filter(dir => dir!=='');
const childDirs = child.split(path.sep).filter(dir => dir!=='');
return papaDirs.every((dir, i) => childDirs[i] === dir);
}
Result in:
assert(isAncestorDir('/path/to/parent', '/path/to/parent/and/child')===true);
assert(isAncestorDir('/path/to/parent', '/path/to')===false);
assert(isAncestorDir('/path/to/parent', '/path/to/parent')===true);
Doing it with regex is one way to go about it (for every path that has an event listener, check whether the published topic starts with that path), but since it's more likely you'll have many different paths than you having absurdly long URLs, breaking down the published topic might be more efficient.
Something like this is probably easier to read, too:
Edit: #huaoguo is definitely right, indexOf === 0 is all we really need!
let paths = [
'test',
'replyer/request',
'test/replyer/request'
]
let topic = 'test/replyer/request/#issuer'
let respondingPaths = (paths, topic) => paths.filter(path => topic.indexOf(path) === 0)
console.log(respondingPaths(paths, topic)) // ['test', 'test/replyer/request']
I would also like to point to the npm package path-is-inside which does exactly what the TO has been asking for:
Usage (quoted from the readme):
Pretty simple. First the path being tested; then the potential parent. Like so:
var pathIsInside = require("path-is-inside");
pathIsInside("/x/y/z", "/x/y") // true
pathIsInside("/x/y", "/x/y/z") // false
Paths are considered to be inside themselves:
pathIsInside("/x/y", "/x/y"); // true
For me, it does the job, and it is for sure a better to maintain such non-trivial logic in an extra package instead of a StackOverflow answer. :-)
#dom-vinyard's idea is good but code is not working correctly, for instance with this input:
isChildOf('/x/y', '/x') //false
I wrote my own version here:
function isParentOf(child, parent) {
const childTokens = child.split('/').filter(i => i.length);
const parentTokens = parent.split('/').filter(i => i.length);
if (parentTokens.length > childTokens.length || childTokens.length === parentTokens.length) {
return false;
}
return childTokens
.slice(0, parentTokens.length)
.every((childToken, index) => parentTokens[index] === childToken);
}
Here another solution that use indexOf (or that work by comparing strings).
In the function bellow i didn't use indexOf in order to support multiple path separators. You can check, but if you are sure you have just one separator, you can use indexOf with no problem.
The trick is to check if the path end with a separator, if not you just add such a separator to it. In that, there will be no problem of having a substring that is not a complete path in the child path. [/this/isme_man and /this/isme] (the first is child of the second, if we simply use indexOf (which of course if false), but if you do using the trick like this [/this/isme/ and /this/isme_man/] and you compare using same indexOf there will be no problem, and it work nikel)].
Notice too that there is an option, to allow a check with orEqual (a child or equal), it's the third optional parameter.
Check the code bellow.
const PATH_SEPA = ['\\', '/'];
function isPathChildOf(path, parentPath, orEqual) {
path = path.trim();
parentPath = parentPath.trim();
// trick: making sure the paths end with a separator
let lastChar_path = path[path.length - 1];
let lastChar_parentPath = path[parentPath.length - 1];
if (lastChar_parentPath !== '\\' && lastChar_parentPath !== '/') parentPath += '/';
if (lastChar_path !== '\\' && lastChar_path !== '/') path += '/';
if (!orEqual && parentPath.length >= path.length) return false; // parent path should be smaller in characters then the child path (and they should be all the same from the start , if they differ in one char then they are not related)
for (let i = 0; i < parentPath.length; i++) {
// if both are not separators, then we compare (if one is separator, the other is not, the are different, then it return false, if they are both no separators, then it come down to comparaison, if they are same nothing happen, if they are different it return false)
if (!(isPathSeparator(parentPath[i]) && isPathSeparator(path[i])) && parentPath[i] !== path[i]) {
return false;
}
}
return true;
}
function isPathSeparator(chr) {
for (let i = 0; i < PATH_SEPA.length; i++) {
if (chr === PATH_SEPA[i]) return true;
}
return false;
}
Here a test example:
let path = '/ok/this/is/the/path';
let parentPath = '/ok/this/is';
let parentPath2 = '/ok/this/is/';
let parentPath3 = '/notok/this/is/different';
console.log("/ok/this/is/the/path' is child of /ok/this/is => " + isPathChildOf(path, parentPath));
console.log("/ok/this/is/the/path' is child of /ok/this/is/=> " + isPathChildOf(path, parentPath2));
console.log("/ok/this/is/' is child of /ok/this/is/ => " + isPathChildOf(parentPath2, parentPath2));
console.log("/ok/this/is/the/path' is child of /notok/this/is/different => " + isPathChildOf(path, parentPath3));
// test number 2:
console.log('test number 2 : ');
console.log("=============================");
let pthParent = '/look/at/this/path';
let pth = '/look/at/this/patholabi/hola'; // in normal use of indexof it will return true (know too we didn't use indexof just to support the different path separators, otherwise we would have used indexof in our function)
//expected result is false
console.log(`${pth} is a child of ${pthParent} ===> ${isPathChildOf(pth, pthParent)}`);
let pthParent2 = '/look/at/this/path';
let pth2 = '/look/at/this/path/hola';
//expected result is true
console.log(`${pth2} is a child of ${pthParent2} ===> ${isPathChildOf(pth2, pthParent2)}`);
let pthParent3 = '/look/at/this/path';
let pth3 = '/look/at/this/pathholabi';
//expected result is false
console.log(`${pth3} is a child of ${pthParent3} ===> ${isPathChildOf(pth3, pthParent3)}`);
// test 3: equality
console.log('\ntest 3 : equality');
console.log("==========================");
let pParent = "/this/is/same/Path";
let p = "/this\\is/same/Path/";
console.log(`${p} is child of ${pParent} ====> ${isPathChildOf(p, pParent, true)}`);
You can see in the last example how we used the function to check for both being a child or equal (which can be really handful).
Also know that, You can check my two related github repos, that include too another implementation for the split method (a spliting method with multiple separator without using the regex engine)), also this method too, and some good explanation (check the comments within the codes):
https://github.com/MohamedLamineAllal/isPathChildOfJS
https://github.com/MohamedLamineAllal/splitStrJS
In my understanding, the issue is a little more complex than what my colleagues understand.
Paths could be tested with fs.existsSync(), but in this case we would create a dependency on the fs library and limit it to testing only absolute directories, both should be tested for their respective existence unless that the user would be interested in not using Windows as the Operating System so as not to need to inform the root directory, which is formed by default by "letter:\" treating the partitions as if they were sisters to each other, no none being nested to a larger directory.
The operation is totally different on Unix systems. The root directory is by default / and all mounted disks are nested to it, making it unique for the entire OS. It can be seen, therefore, that this is not ideal.
It is not possible to solve only with regular expression, considering that, if "pathFoldersLength" is greater than "path2CheckIfIsSupposedParentFoldersLength", a false negative could be obtained if, for example, "path" was equal to "laden /subfolder-1" and "path2CheckIfIsParent" to "bin/laden/subfolder-1/subfolder-1.1" and if the search was done with $ at the end of "path"; leaving without the $ would give a false positive if, for example, "path" was equal to "bin/laden/subfolder-1/subfolder-1.1" and "path2CheckIfIsParent" to "laden/subfolder-1";
If "pathFoldersLength" is less than "path2CheckIfIsSupposedParentFoldersLength, you could get a false negative if, for example, "path" is equal to "laden/subpath-1" and "path2CheckIfIsParent" to "bin/laden/ subpath-1" if the search was done with ^ at the beginning of "path2CheckIfIsParent", or it could give a false positive if, for example, "path" was equal to "bin/laden" and "path2CheckIfIsParent" to " bin/laden/subpath-1".
In this way we eliminate dependencies, leaving the method as little language dependent as possible.
import Path from 'path';
const isASubpathFromAnyOfSubpathSet = (path, path2CheckIfIsParent, isAbsolute = true) => {
const pathSeparatorPattern = new RegExp(/\\+/, 'g');
const pathSeparatorAtStartOrAtEndPattern = new RegExp(/^\/+|\/+$/, 'g');
path = path.replace(pathSeparatorPattern, `/`);
path2CheckIfIsParent = path2CheckIfIsParent.replace(pathSeparatorPattern, `/`);
path = path.replace(pathSeparatorAtStartOrAtEndPattern , ``);
path2CheckIfIsParent = path2CheckIfIsParent.replace(pathSeparatorAtStartOrAtEndPattern , ``)
if (path === path2CheckIfIsParent) return false;
const pathFolders = path.split(`/`);
const path2CheckIfIsSupposedParentFolders = path2CheckIfIsParent.split(`/`);
const pathFoldersLength = pathFolders.length;
const path2CheckIfIsSupposedParentFoldersLength = path2CheckIfIsSupposedParentFolders.length;
const indexesOf = [];
let pathFolderIndex = 0;
let supposedParentFolderIndex = 0;
let stopCriterian;
for (let i = 0; i < path2CheckIfIsSupposedParentFoldersLength; i++) {
if (pathFolders[0] === path2CheckIfIsSupposedParentFolders[i]) indexesOf.push(i);
}
if (indexesOf.length) {
if (isAbsolute) {
if (pathFoldersLength > path2CheckIfIsSupposedParentFoldersLength) {
path2CheckIfIsParent = path2CheckIfIsParent.replace(/\./g, `\\.`);
return (new RegExp(`^${path2CheckIfIsParent}`)).test(path);
}
} else {
if (indexesOf[0] === 0) indexesOf.shift();
if (pathFoldersLength < path2CheckIfIsSupposedParentFoldersLength) {
stopCriterian = () => pathFolderIndex < pathFoldersLength - 1;
} else {
stopCriterian = () => supposedParentFolderIndex < path2CheckIfIsSupposedParentFoldersLength - 1;
}
for (let indexOf of indexesOf) {
pathFolderIndex = 0;
for (supposedParentFolderIndex = indexOf; stopCriterian();) {
if (path2CheckIfIsSupposedParentFolders[supposedParentFolderIndex] !== pathFolders[pathFolderIndex]) break;
supposedParentFolderIndex++;
pathFolderIndex++;
}
}
if (pathFoldersLength < path2CheckIfIsSupposedParentFoldersLength) {
return pathFolderIndex === pathFoldersLength - 1;
} else {
return supposedParentFolderIndex === path2CheckIfIsSupposedParentFoldersLength - 1;
}
}
}
return false;
}
/*
// >
console.log(isASubpathFromAnyOfSubpathSet (`bin/laden/subfolder-1`, `bin/laden`)) // => true
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1/subfolder-1.1.1`, `bin/laden/subfolder-1`)) // => false
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1`, `bin/laden`)) // => false
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1/subfolder-1.1.1`, `bin/laden/subfolder-1`, false)) // => true
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1`, `bin/laden`, false)) // => true
// <
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1`, `bin/laden/subfolder-1/subfolder-1.1`, false)) // => true
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1`, `bin/laden/subfolder-1`, false)) // => true
console.log(isASubpathFromAnyOfSubpathSet (`subfolder-1/subfolder-1.1`, `bin/laden/subfolder-1`, false)) // => true
// ===
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1/subfolder-1.1`, `bin/laden/subfolder-1`, false)) // => true
console.log(isASubpathFromAnyOfSubpathSet (`laden/subfolder-1`, `bin/laden`, false)) // => true
/**/
Use indexOf to check that the path to the child directory starts with the path to the parent directory is enough:
function isParentOf(parent, dir) {
return dir.indexOf(parent) === 0;
}
isParentOf('test/replyer/request/#issuer', 'test') // true
isParentOf('test/replyer/request/#issuer', 'replyer/request') // false
isParentOf('test/replyer/request/#issuer', 'test/replyer/request') // true
Related
I want to use my variable children for different cases:
var children = [];
if (folderPath == '/') {
var children = rootFolder;
} else {
var children = folder.childs;
}
But I get the error message:
variable 'children' must be of type 'any[]' but here has type
'Folder[]'
What does this mean?
In general, if "using a variable for different cases" involves using them for different types, then you're Doing Something Wrong.
Assuming rootFolder is of type Folder, and folder.childs is Folder[], your code looks like it could be something like
const children: Folder[] = (folderPath === '/' ? [rootFolder] : folder.childs);
and in fact you should be able to just do
const children = (folderPath === '/' ? [rootFolder] : folder.childs);
too and let inference handle things.
If you want to use if, then
let children: Folder[];
if (folderPath === '/') {
children = [rootFolder];
} else {
children = folder.childs;
}
should be fine; TypeScript will notice the variable is always definitely set after that if, even if it has no initial value.
I have a set of text files in Google cloud storage(new files come to storage at every 5 minutes[batch processing]). what I want to do is put it into Google BigQuery via Dataflow. In dataflow, we can directly import text files in cloud storage to Google BigQuery(in my case I want batch processing). It requires a javascript code to transform textfiles in GCS into a table in bigquery.
Here is my sample of my one text file.
I want to write javascript code to select topic name is Bat and the values between curly braces into Bigquery table.According to above sample below is the required one.(BigQuery schema will be added later)
Im really new to javascript(actually this is the first time) and I want to implement a javascript function to do this.I don't know select the topic name Bat** in line.
Below is what I tried.(please fix, if this is wrong)
function transform(line) {
const paramsPattern = /[^{\}]+(?=})/g;
let new = line.match(paramsPattern);
var values = new.split(',');
var obj = new Object();
obj.ID = values[0];
obj.AGE = values[1];
var jsonString = JSON.stringify(obj);
return jsonString;
}
Thanks in advance !!!
If it comes to reliability, as well as convenience and/or maintainability one should think about a more generic approach that does reduce a list of lines/strings and in addition takes even a specific topic into account by assembling the correct regex ...
const fileData = `
Topic:Cat [0] at offset:216712{"ID":55689534,"NAME":6}
Topic:Cat [1] at offset:216719{"ID":55689524,"NAME":6}
Topic : Bat [0] at offset:216716 {"CODE":94762151097,"AGE":32}
Topic:Cat [0] at offset:216713{"ID":55689524,"NAME":6}
Topic:Bat [1] at offset:216723{"CODE":947080272531,"AGE":43}
Topic:Cat [1] at offset:216738{"ID":55689525,"NAME":6}
`;
const dataItemList = fileData.split(/\n/);
function getTopicSpecificDataCaptureRegX(topic) {
// see also: [https://regex101.com/r/AD31R6/1/]
//return (/^\s*Topic\s*\:\s*Bat[^{]+(\{.*\})\s*$/);
//return (/^\s*Topic\s*\:\s*Cat[^{]+(\{.*\})\s*$/);
return RegExp('^\\s*Topic\\s*\\:\\s*' + topic + '[^{]+(\\{.*\\})\\s*$');
}
function collectTopicSpecificData(collector, dataItem) {
const result = dataItem.match(collector.regX);
if (result !== null) {
collector.list.push(JSON.parse(result[1]));
}
return collector;
}
console.log(
'"Cat" specific data list : ',
dataItemList.reduce(collectTopicSpecificData, {
regX: getTopicSpecificDataCaptureRegX('Cat'),
list: []
}).list
);
console.log(
'"Bat" specific data list : ',
dataItemList.reduce(collectTopicSpecificData, {
regX: getTopicSpecificDataCaptureRegX('Bat'),
list: []
}).list
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
let new = line.match(paramsPattern);
you should not assign this to a variable new... new is special.
This is a regex that would work with your example: https://regex101.com/r/tBHRIY/1
Here is an example:
const testLine = 'Topic:Bat [0] at offset:216812{"ID":51255125, "NAME":6}';
function transform(line) {
const paramsPattern = /({[\s\S]+})/g;
const match = line.match( paramsPattern );
if ( line.indexOf( 'Bat' ) === -1 )
return null;
if ( match === null )
return null;
// This will verify that the json is valid ( it will throw ) but you can skip this and return match[0] directly if you are sure it is valid
return JSON.stringify( JSON.parse( match[0] ) );
}
console.log( transform( testLine ) );
Edit: sorry i missed checking for BAT, added
I'm looking for a clean way to get true or false based on an order of permissions based on the following rules:
Starts with Company Permissions as a default
Then to Team Permissions if permission defined
Finally to User Permissions if permission is
This would need to also handle undefined. So basically wanting to see if there's some "clean" way to do this without having to conditionally check each value and moving on.
In this example, the result should be false since there are no User Permissions defined and the Team Permissions has false.
const UserPermissions = {}
const TeamPermissions = {
PERMISSION_ONE: false
}
const CompanyPermissions = {
PERMISSION_ONE: true
}
const hasPermissions = UserPermissions.PERMISSION_ONE || TeamPermissions.PERMISSION_ONE || CompanyPermissions.PERMISSION_ONE
console.log(hasPermissions)
Thanks!
From my understanding, the rules are:
ignore undefined
return true of false, whatever comes first
This little function should handle that, not sure how you want to deal with empty (or all undefined) arguments.
let x;
let t = true;
let f = false;
let firstBool = (...a) => Boolean(a.filter(x => typeof x !== 'undefined').shift());
console.log(firstBool(x,t,f));
console.log(firstBool(t,x,f));
console.log(firstBool(x,f,t));
console.log(firstBool());
In your example, that would be
const hasPermissions = firstBool(
UserPermissions.PERMISSION_ONE,
TeamPermissions.PERMISSION_ONE,
CompanyPermissions.PERMISSION_ONE
]
If you are looking for the same property name in multiple objects, you might be able to slightly alter the technique in georg's answer:
const firstBoolProp = (...objs) => (prop) =>
objs.reduce((a, o) => Boolean(a) === a ? a : o[prop], undefined)
const UserPermissions = {}
const TeamPermissions = {PERMISSION_ONE: false}
const CompanyPermissions = {PERMISSION_ONE: true}
console .log (
firstBoolProp
(UserPermissions, TeamPermissions, CompanyPermissions)
('PERMISSION_ONE')
)
You can then use a single function to multiple permissions against that same set of permission objects:
const getPermissions = firstBoolProp(UserPermissions, TeamPermissions, CompanyPermissions)
const perms = {
p1: getPermissions('PERMISSION_ONE'),
p2: getPermissions('PERMISSION_TWO'),
}
//=> {p1: false, p2: undefined}
And if you want to use an array rather than individual parameters, you can simply replace (...obj) => with (obj) =>
One way to do this is to store the permissions in an array and reduce it to a boolean:
/**
*
* #param {{PERMISSION_ONE:boolean}[]} permissions
*/
function anyoneHasPermission(permissions, name = "PERMISSION_ONE") {
for (const perm of permissions) {
if (perm[name]) {
return true;
}
}
}
console.log(anyoneHasPermission([UserPermissions, TeamPermissions, CompanyPermissions]))
You need to be more specific in what you want to accomplish. Choosing the right data structure is usually more important than choosing an algorithm. Indeed, sometimes the right data structure makes the algorithm disappear completely.
Throw the permissions into an array in the order that you want them validated.
Iterate the array and if any of your conditions is not met then return false and break.
This will stop you running down the entire chain if say your top level permission is not set (because nothing beyond that matters anymore and no use validating it)
const UserPermissions = {PERMISSION_ONE: true}
const TeamPermissions = {}
const CompanyPermissions = {PERMISSION_ONE: true}
const permCheck = HasPerms([CompanyPermissions, TeamPermissions, UserPermissions]);
alert(permCheck);
function HasPerms(permArray){
for (var i = 0; i < permArray.length; i++) {
if (!permArray[i] || !permArray[i].PERMISSION_ONE) {return false;}
}
return true;
}
You can expand the function to dynamically take in the permission you would like to check but example shown hardcoded for simplicity sake.
So, I'm writing a client-side search and I need to look through strings of Japanese characters. I'm wondering how to do this properly?... i.e. Do I change the format of the text into utf-8 something and then search the utf-8?
Example:
All my data has japaneseData.title : "フェリーチェ三田"
When I type in my search.value as : "フェ" using japaneseData.title.includes(search.value) I don't get a match...
How do I do this correctly?
Okay, after further inspection, the comments were correct and includes was finding the substring. This is all happening inside of a filter() and I'm trying to return the objects that match...
After changing my code to:
let filteredArrayofObjects = Lists.houseLists.filter(house => house.building_name.includes(query.search));
I was getting back some but not all. Problem cases:
"アーバイルスパシエ芝浦BAY-SIDE".includes("エ芝浦"); // this evaluates to true, but does not get included in my filtered array...
Okay, further digging, it seems the issue is I need to wait for the filter process before returning the results... haven't yet found a solution to that just yet.
async filter(arr, callback) {
return (await Promise.all(
arr.map(async item => {
return (await callback(item)) ? item : undefined;
})
)).filter(i => i !== undefined);
}
handleFilterLists = async (query = {}) => {
const { Lists } = this.props;
let searchResults = await this.filter(Lists.houseLists, async house => {
return house.building_name.includes(query.search);
// the final evaluation to look similar to this:
// var newArray = homes.filter(function (el) {
// return el.price <= 1000 &&
// el.sqft >= 500 &&
// el.num_of_beds >=2 &&
// el.num_of_baths >= 2.5;
// });
});
this.setState({ searchResults });
}
Okay, so, I'm trying to set state.searchResults after the filter method has checked for matching objects in the array Lists.houseLists...
includes returns true or false if the substring is detected or not. If you want the index of where the first detected substring begins, use indexOf.
I used your sample source and search text with includes and it returns true.
Edit:
I used your updated data and this still works. https://codepen.io/anon/pen/RMWpwe
const sourceText = 'アーバイルスパシエ芝浦BAY-SIDE';
const searchText = 'エ芝浦';
const lists = [
'スパシエ',
'芝浦BAY-SIDE',
'エ芝浦',
'パシエ芝浦BAY'
];
console.log(lists.filter(item => item.includes(searchText)));
// ["エ芝浦", "パシエ芝浦BAY"]
Given an array of randomly sorted paths, I'd like to filter it so only the shallowest paths stay in the array. Any sub-paths should be removed.
My attempt below does filter some paths, but the end result is wrong and should be an array containing only these:
[ '/video', '/audio', '/code', '/images', '/test/noparent' ]
var paths = [
'/code/client/images',
'/code/client/templates/views',
'/code/another/templates/views',
'/code/client/svg',
'/images',
'/code/another',
'/code/client',
'/audio/asdasd',
'/audio/asd',
'/code/client/html',
'/video',
'/code/client/templates',
'/audio/asd/asdasd',
'/code/another/html',
'/code/another/images',
'/code/another/svg',
'/code/another/templates',
'/code',
'/audio',
'/test/noparent'
];
// prepare by sorting paths by number of slashes
paths.sort(function (a, b) {
return a.match(/\//g).length - b.match(/\//g).length;
});
// filter (this fails)
paths.filter(function (path) {
var keep = true;
paths.forEach(function (another) {
if (another.indexOf(path) === 0 && another !== path) keep = false;
});
return keep;
});
Maybe there's a solution that doesn't iterate more than once?
You need to reverse the existence of the strings. Check if the current path is a one which no other path fits (indexOf) inside of it.
paths.filter(function (path) {
var keep = true;
paths.forEach(function (another) {
if (path.indexOf(another) === 0 && another !== path) keep = false;
});
return keep;
});
Here's the solution I just camp up with:
paths.filter(function (path) {
return paths.every(function (another) {
return another === path || path.indexOf(another) !== 0;
});
});