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
.
share|improve this question
    
"You can assume the input current directory has a path separator / at the end". However, in your examples, this is not the case. – Shaggy 12 hours ago
1  
I like it this way, but some people like it the other way – anatolyg 12 hours ago
    
Closely related. – AdmBorkBork 12 hours ago
    
What should happen if absolute and relative path have the same length? – Dennis 10 hours ago
    
If same length, either output is good. – anatolyg 9 hours ago

JavaScript (ES6), 107 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=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=s+A.join`/`||'.')[a.length]?a:s

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

console.log(f
  ('/var/users/admin/secret/passwd')
  ('/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')
);

share|improve this answer
    
A very nice trick with [a.length] ! May I borrow it to improve my Node.js answer ? – zeppelin 7 hours ago
    
@zeppelin Sure. Go for it! – Arnauld 7 hours ago

Retina, 85 bytes

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

;
/
.*//
/
%/|%|/$

^$
.

Try it online!

Will golf further in the morning

share|improve this answer

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!

share|improve this answer

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

  • 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("/var/users/admin/secret/passwd")
'../../admin/secret/passwd'

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

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

> F("/dir1/dir2")
'.'
share|improve this answer

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> !,~=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("/var/users/admin/secret/passwd","/var/users/joe/hack")
"../../admin/secret/passwd"

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!

share|improve this answer

Python 2, 135 bytes

i=0
a,c=[j.split('/')for j in input()]
if a==c:a=[]
while a[:i+1]==c[:i+1]:i+=1
print'/'.join([['..']*len(c[i:]),['']][i<2]+a[i:])or'.'

Try it Online!

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

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.