# Shorten an absolute path

Sometimes a long absolute path, in e.g. a command-line parameter to a linux tool, can be shortened, using current working directory as reference:

$pwd /home/heh$ cat /home/heh/mydir/myfile
my stuff

$cat mydir/myfile my stuff  In this challenge, you should make a function or a program that receives two parameters: 1. Absolute path, using the linux format (starts with /) 2. Current directory, using the same format The output is the shorter of the following: • Input 1 unchanged • Relative path that refers to the same file/directory as the absolute path Fine points: • If your operating system is compatible with linux, you can use the system's current directory instead of receiving it as input • You can assume the inputs contain only alphanumeric characters (and path separators) • You can assume the input absolute path doesn't have a path separator / at the end • You can assume the input current directory has a path separator / at the end • You cannot assume that the absolute path refers to an existing file, or that any part of it is an accessible directory; however, the current directory can be assumed valid • You can assume there are no symlinks anywhere near either path - because I don't want to require any special way of dealing with symlinks • No need to support the case where either of the inputs is the root directory • "The current directory" should be output as . (an empty string is not valid) Test cases (input1, input2, output): /home/user/mydir/myfile /home/user mydir/myfile /var/users/admin/secret/passwd /var/users/joe/hack ../../admin/secret/passwd /home/user/myfile /tmp/someplace /home/user/myfile /dir1/dir2 /dir1/dir2/dir3/dir4 ../.. /dir1/dir2 /dir1/dir2 .  • "You can assume the input current directory has a path separator / at the end". However, in your examples, this is not the case. – Shaggy Apr 19 '17 at 16:30 • I like it this way, but some people like it the other way – anatolyg Apr 19 '17 at 16:33 • – AdmBorkBork Apr 19 '17 at 17:01 • What should happen if absolute and relative path have the same length? – Dennis Apr 19 '17 at 19:00 • This is missing some critical test cases: /home/test /home/user/mydir/myfile /home/test and /a/b /a/b/d/e /a/b – Nathan Merrill Apr 20 '17 at 16:38 ## 9 Answers # Julia 0.5, 32 bytes !,~=relpath,endof t->~t<~!t?t:!t  This uses the current working directory as base and cannot be tested on TIO at the moment. ### Example run Warning: This will alter your file system. $ sudo julia --quiet
julia> function test(target,base)
mkpath(base)
cd(base)
shorten(target)
end
test (generic function with 1 method)
julia> !,~=relpath,endof
(relpath,endof)

julia> shorten = t->~t<~!t?t:!t
(::#1) (generic function with 1 method)

julia> test("/home/user/mydir/myfile","/home/user")
"mydir/myfile"

julia> test("/home/user/myfile","/tmp/someplace")
"/home/user/myfile"

julia> test("/dir1/dir2","/dir1/dir2/dir3/dir4")
"../.."

julia> test("/dir1/dir2","/dir1/dir2")
"."


## Alternate version, 35 bytes (dyadic)

^,~=relpath,endof
t-b=~t<~t^b?t:t^b


This takes the base directory as input, so it can be tested without modifying the file system.

Try it online!

• Redefining Base.- errors unless explicitly imported, no? – Julian Wolf Apr 20 '17 at 23:43
• In 0.5, it may error, but only if you use - before redefining it. In 0.4, it prints a warning whether you use it before the redefinition or not. – Dennis Apr 21 '17 at 0:59

## JavaScript (ES6), 107 106 bytes

Takes the absolute path a and the current path c in currying syntax (a)(c).

a=>c=>(A=a.split/,s='',c.split/.map(d=>!s&A[0]==d?A.shift():s+='../'),s+=A.join/)[a.length]?a:s||'.'


### Test cases

let f =

a=>c=>(A=a.split/,s='',c.split/.map(d=>!s&A[0]==d?A.shift():s+='../'),s+=A.join/)[a.length]?a:s||'.'

console.log(f
('/home/user/mydir/myfile')
('/home/user')
);

console.log(f
('/var/users/joe/hack')
);

console.log(f
('/home/user/myfile')
('/tmp/someplace')
);

console.log(f
('/dir1/dir2')
('/dir1/dir2/dir3/dir4')
);

console.log(f
('/dir1/dir2')
('/dir1/dir2')
);

• A very nice trick with [a.length] ! May I borrow it to improve my Node.js answer ? – zeppelin Apr 19 '17 at 21:33
• @zeppelin Sure. Go for it! – Arnauld Apr 19 '17 at 21:55

# Retina, 8583 82 bytes

1 byte saved thanks to @MartinEnder

^(..+)(.*;)\1
%$2 (%?)(.*);(.*)$1$3;$2
\w+(?=.*;)
..
%;/

;
/
.*//
/
%/?|/$^$
.


Try it online!

# ES6 (Node.js REPL), 56, 54, 46, 45 bytes

• Use empty string, instead of "." to denote the current directory (on input), -1 byte
• Borrowed the [f.length] trick from @Arnauld's answer, -6 bytes
• Use the current directory instead of an explicit directory parameter, -2 bytes
• Removed superfluous parentheses, -2 bytes

Golfed

f=>(r=path.relative("",f))[f.length]?f:r||"."


Test

> F=f=>(r=path.relative("",f))[f.length]?f:r||"."
[Function: F]

> F("/home/user/mydir/myfile")
'mydir/myfile'

> F("/home/user/myfile")
'/home/user/myfile'

> F("/dir1/dir2")
'../..'

> F("/dir1/dir2")
'.'

• Do we not allow node.js functions? – Downgoat Apr 20 '17 at 14:06
• @Downgoat Javascript lambdas are widely accepted, as a form of answer, so I don't see why Node.js should be handled differently. – zeppelin Apr 20 '17 at 14:27

# Python 2, 135 144 bytes

i=0
a,c=input()
b,d=a.split('/')*(a!=c),c.split('/')
while b[:i+1]==d[:i+1]:i+=1
print'.'[i:]or min('/'.join(['..']*len(d[i:])+b[i:]),a,key=len)


Try it Online!

Kind of long, but I wanted to do a solution without built-in path functions.

Edit: 9 bytes added to account for test case provided by Nathan Merrill

# Zsh + realpath, 58 bytes

r=realpath -m --relative-to=$* (($#2<$#r))&&r=$2
echo $r  Try it online! ### Bash version, 62 bytes r=realpath -m --relative-to=$*
((${#2}<${#r}))&&r=$2 echo$r


Try it online!

• Why not post it in two different answers? Every language matters! – gaborsch Apr 20 '17 at 12:36

## Python 3 – 53 bytes

Using os.path:

import os
lambda x:min(x,os.path.relpath(x),key=len)


Full program (61 bytes):

import os
x=input();print(min(x,os.path.relpath(x),key=len))

• Oo, good point(s). Python's in the lead now, yay! – matsjoyce Apr 20 '17 at 13:43
• @anatolyg Ha, I knew I'd miss at least one test case... 😒 All fixed now. – matsjoyce Apr 20 '17 at 16:27

# PHP, 204 Bytes

[,$l,$p]=$argv;$z=($d=array_diff_assoc)($y=($w=explode)("/",$p),$x=$w("/",$l));$m=str_pad("",3*count($z)-1,"../");$j=join("/",$r=$d($x,$y));echo$l!=$p?strlen($u=$m&&$j?"$m/$j":$m.$j)<strlen($l)?$u:$l:".";


Testcases

Expanded

[,$l,$p]=$argv;$z=($d=array_diff_assoc)($y=($w=explode)("/",$p),$x=$w("/",$l));$m=str_pad("",3*count($z)-1,"../");$j=join("/",$r=$d($x,$y));
echo$l!=$p
?strlen($u=$m&&$j?"$m/$j":$m.$j)<strlen($l)
?$u :$l
:".";


if an Output ../../ instead of ../.. is allowed it can be shorten to 175 Bytes

[,$l,$p]=$argv;$z=($d=array_diff_assoc)($y=($w=explode)("/",$p),$x=$w("/",$l));echo$l!=$p?strlen($m=str_pad("",3*count($z),"../").join("/",$r=$d($x,$y)))<strlen($l)?$m:$l:".";


# C# - 66 bytes

Using a .NET builtin and forcing to be a valid path:

(f,t)=>f==t?".":new Uri("/"+t).MakeRelativeUri(new Uri("/"+f))+"";


Where f,t and output are string.

Try it online!