Is there a built-in javascript (client-side) function that functions similarly to Node's path.join? I know I can join strings in the following manner:

['a', 'b'].join('/') 

The problem is that if the strings already contain a leading/trailing "/", then they will not be joined correctly, e.g.:

['a/','b'].join('/') 
1

10 Answers

Use the path module. path.join is exactly what you're looking for. From the docs:

path.join([path1][, path2][, ...])# Join all arguments together and normalize the resulting path.

Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown.

Example:

const path = require('node:path') path.join('/foo', 'bar', 'baz/asdf', 'quux', '..') // returns '/foo/bar/baz/asdf' path.join('foo', {}, 'bar') // throws exception TypeError: Arguments to path.join must be strings 

You can also use import path from 'path' instead of const path = require('node:path') if you're loading modules with that style.

Edit:

I assumed here that you're using server-side Javascript like node.js. If you want to use it in the browser, you can use path-browserify.

2

Building on @Berty's reply, this ES6 variant preserves all leading slashes, to work with protocol relative url's (like //stackoverflow.com), and also ignores any empty parts:

build_path = (...args) => { return args.map((part, i) => { if (i === 0) { return part.trim().replace(/[\/]*$/g, '') } else { return part.trim().replace(/(^[\/]*|[\/]*$)/g, '') } }).filter(x=>x.length).join('/') } 
  • build_path("", "my", "path") will return ""
  • build_path("//a", "", "/", "/b/") will return "//a/b"
  • build_path() will return ""

Note that this regex strips trailing slashes. Sometimes a trailing slash carries semantic meaning (e.g. denoting a directory rather than a file), and that distinction will be lost here.

2

There isn't currently a built-in that will perform a join while preventing duplicate separators. If you want concise, I'd just write your own:

function pathJoin(parts, sep){ var separator = sep || '/'; var replace = new RegExp(separator+'{1,}', 'g'); return parts.join(separator).replace(replace, separator); } var path = pathJoin(['a/', 'b', 'c//']) 
2

The accepted answer doesn't work for URLs, it removes the double slash after the protocol
becomes https:/hostname.

Most other answers do not handle the first and last part differently. A slash at the beginning or end should not be removed, it would change the meaning (relative/absolute) (file/directory) of the joined path.

Below is a modified version of the accepted answer:

function pathJoin(parts, sep){ const separator = sep || '/'; parts = parts.map((part, index)=>{ if (index) { part = part.replace(new RegExp('^' + separator), ''); } if (index !== parts.length - 1) { part = part.replace(new RegExp(separator + '$'), ''); } return part; }) return parts.join(separator); } 

usage:

console.log(pathJoin([' 'hostname', 'path/'])); // ' console.log(pathJoin(['relative/', 'path', 'to/dir/'])); // 'relative/path/to/dir/' console.log(pathJoin(['/absolute/', 'path', 'to/file'])); // '/absolute/path/to/file' 

4

My approach to solve this problem:

var path = ['a/','b'].map(function (i) { return i.replace(/(^\/|\/$)/, ''); }).join('/'); 

Second method:

var path = ['a/','b'].join('/').replace(/\/{2,}/, '/') 
3

You may find the code on this gist "Simple path join and dirname functions for generic javascript" useful (i.e both in node and browser)

// Joins path segments. Preserves initial "/" and resolves ".." and "." // Does not support using ".." to go above/outside the root. // This means that join("foo", "../../bar") will not resolve to "../bar" function join(/* path segments */) { // Split the inputs into a list of path commands. var parts = []; for (var i = 0, l = arguments.length; i < l; i++) { parts = parts.concat(arguments[i].split("/")); } // Interpret the path commands to get the new resolved path. var newParts = []; for (i = 0, l = parts.length; i < l; i++) { var part = parts[i]; // Remove leading and trailing slashes // Also remove "." segments if (!part || part === ".") continue; // Interpret ".." to pop the last segment if (part === "..") newParts.pop(); // Push new path segments. else newParts.push(part); } // Preserve the initial slash if there was one. if (parts[0] === "") newParts.unshift(""); // Turn back into a single string path. return newParts.join("/") || (newParts.length ? "/" : "."); } // A simple function to get the dirname of a path // Trailing slashes are ignored. Leading slash is preserved. function dirname(path) { return join(path, ".."); } 

Note similar implementations (which may be transformed to js code as well) exist for php here

2

There is not, however it is pretty easy to implement. This could also be solved with a regex but its not too bad without one.

var pathJoin = function(pathArr){ return pathArr.map(function(path){ if(path[0] === "/"){ path = path.slice(1); } if(path[path.length - 1] === "/"){ path = path.slice(0, path.length - 1); } return path; }).join("/"); } 

This one makes sure it works with http:// links without removing the double slash. It trims the slashes at the beginning and end of each part. Then joins them seperated by '/'

/** * Joins 2 paths together and makes sure there aren't any duplicate seperators * @param parts the parts of the url to join. eg: [' '/my-custom/path/'] * @param separator The separator for the path, defaults to '/' * @returns {string} The combined path */ function joinPaths(parts, separator) { return parts.map(function(part) { return part.trim().replace(/(^[\/]*|[\/]*$)/g, ''); }).join(separator || '/'); } 

Building on what @leo did:

export function buildPath(...args: string[]): string { const [first] = args; const firstTrimmed = first.trim(); const result = args .map((part) => part.trim()) .map((part, i) => { if (i === 0) { return part.replace(/[/]*$/g, ''); } else { return part.replace(/(^[/]*|[/]*$)/g, ''); } }) .filter((x) => x.length) .join('/'); return firstTrimmed === '/' ? `/${result}` : result; } 

Should cover following scenarios:

 [ { input: ['/'], result: '/', }, { input: ['/', 'aaa', ':id'], result: '/aaa/:id', }, { input: ['/bbb', ':id'], result: '/bbb/:id', }, { input: ['ccc', ':id'], result: 'ccc/:id', }, { input: ['/', '/', '/', '/ddd/', '/', ':id'], result: '/ddd/:id', }, { input: ['', '', '', 'eee', '', ':id'], result: 'eee/:id', }, ]; 

All of the other solutions contain bugs, are difficult to understand, and support features that are redundant in the browser, so I wrote my own (derived from the others). This version adds support for file URLs and removes support for Windows paths.

const join = function(...parts) { /* This function takes zero or more strings, which are concatenated together to form a path or URL, which is returned as a string. This function intelligently adds and removes slashes as required, and is aware that `file` URLs will contain three adjacent slashes. */ const [first, last, slash] = [0, parts.length - 1, "/"]; const matchLeadingSlash = new RegExp("^" + slash); const matchTrailingSlash = new RegExp(slash + "$"); parts = parts.map(function(part, index) { if (index === first && part === "file://") return part; if (index > first) part = part.replace(matchLeadingSlash, ""); if (index < last) part = part.replace(matchTrailingSlash, ""); return part; }); return parts.join(slash); }; 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy