Find common directory path: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
(34 intermediate revisions by 20 users not shown)
Line 17: Line 17:
{{trans|C}}
{{trans|C}}


<lang 11l>F find_common_directory_path(paths, sep = ‘/’)
<syntaxhighlight lang="11l">F find_common_directory_path(paths, sep = ‘/’)
V pos = 0
V pos = 0
L
L
Line 34: Line 34:
‘/home/user1/tmp/coverage/test’,
‘/home/user1/tmp/coverage/test’,
‘/home/user1/tmp/covert/operator’,
‘/home/user1/tmp/covert/operator’,
‘/home/user1/tmp/coven/members’]))</lang>
‘/home/user1/tmp/coven/members’]))</syntaxhighlight>


{{out}}
{{out}}
Line 42: Line 42:


=={{header|Ada}}==
=={{header|Ada}}==
<lang ada>
<syntaxhighlight lang="ada">
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Text_IO; use Ada.Text_IO;


Line 80: Line 80:
);
);
end Test_Common_Path;
end Test_Common_Path;
</syntaxhighlight>
</lang>
Output:
Output:
<pre>
<pre>
Line 87: Line 87:


=={{header|Aime}}==
=={{header|Aime}}==
<lang aime>cdp(...)
<syntaxhighlight lang="aime">cdp(...)
{
{
integer e;
integer e;
Line 111: Line 111:


0;
0;
}</lang>
}</syntaxhighlight>


=={{header|ALGOL 68}}==
=={{header|ALGOL 68}}==
Line 120: Line 120:
{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release [http://sourceforge.net/projects/algol68/files/algol68toc/algol68toc-1.8.8d/algol68toc-1.8-8d.fc9.i386.rpm/download 1.8-8d]}}
{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release [http://sourceforge.net/projects/algol68/files/algol68toc/algol68toc-1.8.8d/algol68toc-1.8-8d.fc9.i386.rpm/download 1.8-8d]}}


<lang algol68># Utilities code #
<syntaxhighlight lang="algol68"># Utilities code #


CHAR dir sep = "/"; # Assume POSIX #
CHAR dir sep = "/"; # Assume POSIX #
Line 177: Line 177:
);
);
print((dir name(common prefix(dir list)), new line))
print((dir name(common prefix(dir list)), new line))
)</lang>
)</syntaxhighlight>
Output:
Output:
<pre>
<pre>
/home/user1/tmp
/home/user1/tmp
</pre>
</pre>

=={{header|AppleScript}}==
<syntaxhighlight lang="applescript">on commonDirectoryPath(thePaths, separator)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to separator
set path1 to thePaths's beginning
set path1Components to path1's text items
set maxC to (count path1Components)
repeat with nextPath in (thePaths's rest)
if (maxC = 0) then exit repeat
set theseComponents to nextPath's text items
set componentCount to (count theseComponents)
if (componentCount < maxC) then set maxC to componentCount
repeat with c from 1 to maxC
if (theseComponents's item c ≠ path1Components's item c) then
set maxC to c - 1
exit repeat
end if
end repeat
end repeat
if (maxC > 0) then
set commonPath to path1's text 1 thru text item maxC
else
set commonPath to ""
end if
set AppleScript's text item delimiters to astid
return commonPath
end commonDirectoryPath

return commonDirectoryPath({"/home/user1/tmp/coverage/test", ¬
"/home/user1/tmp/covert/operator", ¬
"/home/user1/tmp/coven/members"}, "/")</syntaxhighlight>

{{output}}
<syntaxhighlight lang="applescript">"/home/user1/tmp"</syntaxhighlight>

=={{header|Arturo}}==

<syntaxhighlight lang="rebol">commonPathPrefix: function [lst][
paths: map lst => [split.by:"/" &]

common: new []
firstPath: first paths
loop .with:'i firstPath 'part [
found: true
loop paths 'p [
if part <> get p i [
found: false
break
]
]
if found -> 'common ++ part
]
return join.with:"/" common
]

print commonPathPrefix [
"/home/user1/tmp/coverage/test"
"/home/user1/tmp/covert/operator"
"/home/user1/tmp/coven/members"
]</syntaxhighlight>

{{out}}

<pre>/home/user1/tmp</pre>


=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==
<lang autohotkey>Dir1 := "/home/user1/tmp/coverage/test"
<syntaxhighlight lang="autohotkey">Dir1 := "/home/user1/tmp/coverage/test"
Dir2 := "/home/user1/tmp/covert/operator"
Dir2 := "/home/user1/tmp/covert/operator"
Dir3 := "/home/user1/tmp/coven/members"
Dir3 := "/home/user1/tmp/coven/members"
Line 198: Line 264:
Else Break
Else Break


MsgBox, % Result</lang>
MsgBox, % Result</syntaxhighlight>
Message box shows:
Message box shows:
<pre>/home/user1/tmp</pre>
<pre>/home/user1/tmp</pre>


=={{header|AWK}}==
=={{header|AWK}}==
<lang awk># Finds the longest common directory of paths[1], paths[2], ...,
<syntaxhighlight lang="awk"># Finds the longest common directory of paths[1], paths[2], ...,
# paths[count], where sep is a single-character directory separator.
# paths[count], where sep is a single-character directory separator.
function common_dir(paths, count, sep, b, c, f, i, j, p) {
function common_dir(paths, count, sep, b, c, f, i, j, p) {
Line 243: Line 309:
a[3] = "/home/user1/tmp/coven/members"
a[3] = "/home/user1/tmp/coven/members"
print common_dir(a, 3, "/")
print common_dir(a, 3, "/")
}</lang>
}</syntaxhighlight>


Prints <tt>/home/user1/tmp</tt>.
Prints <tt>/home/user1/tmp</tt>.
Line 255: Line 321:
Also, under FreeBASIC, the <code>pathSep</code> arg to <code>commonPath$</code> could be made optional, or even system-dependent.
Also, under FreeBASIC, the <code>pathSep</code> arg to <code>commonPath$</code> could be made optional, or even system-dependent.


<lang qbasic>DECLARE FUNCTION commonPath$ (paths() AS STRING, pathSep AS STRING)
<syntaxhighlight lang="qbasic">DECLARE FUNCTION commonPath$ (paths() AS STRING, pathSep AS STRING)


DATA "/home/user2", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"
DATA "/home/user2", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"
Line 306: Line 372:
commonPath$ = tmpstr1
commonPath$ = tmpstr1
END IF
END IF
END FUNCTION</lang>
END FUNCTION</syntaxhighlight>

=={{header|BASIC256}}==
{{trans|GW-BASIC}}
<syntaxhighlight lang="basic256">x = "/home/user1/tmp/coverage/test"
y = "/home/user1/tmp/covert/operator"
z = "/home/user1/tmp/coven/members"

a = length(x)
if a > length(y) then a = length(y)
if a > length(z) then a = length(z)
for i = 1 to a
if mid(x, i, 1) <> mid(y, i, 1) then exit for
next i
a = i - 1

for i = 1 to a
if mid(x, i, 1) <> mid(z, i, 1) then exit for
next i
a = i - 1

if mid(x, i, 1) <> "/" then
for i = a to 1 step -1
if "/" = mid(x, i, 1) then exit for
next i
end if

REM Task description says no trailing slash, so...
a = i - 1
print "Common path is '"; left(x, a); "'"</syntaxhighlight>


=={{header|Batch File}}==
=={{header|Batch File}}==
<lang dos>
<syntaxhighlight lang="dos">
@echo off
@echo off
setlocal enabledelayedexpansion
setlocal enabledelayedexpansion
Line 371: Line 466:
endlocal
endlocal
exit /b
exit /b
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 378: Line 473:


=={{header|BBC BASIC}}==
=={{header|BBC BASIC}}==
<lang bbcbasic> DIM path$(3)
<syntaxhighlight lang="bbcbasic"> DIM path$(3)
path$(1) = "/home/user1/tmp/coverage/test"
path$(1) = "/home/user1/tmp/coverage/test"
Line 396: Line 491:
NEXT J%
NEXT J%
UNTIL I% = 0
UNTIL I% = 0
= LEFT$(p$(1), O%-1)</lang>
= LEFT$(p$(1), O%-1)</syntaxhighlight>


=={{header|C}}==
=={{header|C}}==
<lang C>#include <stdio.h>
<syntaxhighlight lang="c">#include <stdio.h>


int common_len(const char *const *names, int n, char sep)
int common_len(const char *const *names, int n, char sep)
Line 432: Line 527:


return 0;
return 0;
}</lang>output:<lang>Common path: /home/user1/tmp</lang>
}</syntaxhighlight>output:<syntaxhighlight lang="text">Common path: /home/user1/tmp</syntaxhighlight>


=={{header|C sharp|C#}}==
=={{header|C sharp|C#}}==


<lang csharp>
<syntaxhighlight lang="csharp">
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
Line 500: Line 595:
}
}


</syntaxhighlight>
</lang>


=={{header|C++}}==
=={{header|C++}}==
<lang cpp>#include <algorithm>
<syntaxhighlight lang="cpp">#include <algorithm>
#include <iostream>
#include <iostream>
#include <string>
#include <string>
Line 533: Line 628:
std::string::size_type found = compareString.rfind( separator , maxCharactersCommon ) ;
std::string::size_type found = compareString.rfind( separator , maxCharactersCommon ) ;
return compareString.substr( 0 , found ) ;
return compareString.substr( 0 , found ) ;
}</lang>
}</syntaxhighlight>
Output:
Output:
<pre>
<pre>
Line 540: Line 635:


=={{header|Clojure}}==
=={{header|Clojure}}==
<lang clojure>(use '[clojure.string :only [join,split]])
<syntaxhighlight lang="clojure">(use '[clojure.string :only [join,split]])


(defn common-prefix [sep paths]
(defn common-prefix [sep paths]
Line 553: Line 648:
["/home/user1/tmp/coverage/test"
["/home/user1/tmp/coverage/test"
"/home/user1/tmp/covert/operator"
"/home/user1/tmp/covert/operator"
"/home/user1/tmp/coven/members"]))</lang>
"/home/user1/tmp/coven/members"]))</syntaxhighlight>


=={{header|Common Lisp}}==
The strings represent file paths, so instead of treating them as simple strings, this uses the specialized pathname functions, which are more robust and generic.
<syntaxhighlight lang="lisp">
(defun common-directory-path (&rest paths)
(do* ((pathnames (mapcar #'(lambda (path) (cdr (pathname-directory (pathname path)))) paths)) ; convert strings to lists of subdirectories
(rem pathnames (cdr rem))
(pos (length (first rem))) ) ; position of first mismatched element
((null (cdr rem)) (make-pathname :directory (cons :absolute (subseq (first pathnames) 0 pos)))) ; take the common sublists and convert back to a pathname
(setq pos (min pos (mismatch (first rem) (second rem) :test #'string-equal))) )) ; compare two paths
</syntaxhighlight>


{{out}}
<pre>(common-directory-path "/home/user1/tmp/coverage/test" "/home/user1/tmp/covert/operator" "/home/user1/tmp/coven/members")
==> #P"/home/user1/tmp/"</pre>


=={{header|D}}==
=={{header|D}}==
This code uses the std.algorithm.commonPrefix function that finds the common prefix of two ranges.
This code uses the std.algorithm.commonPrefix function that finds the common prefix of two ranges.
<lang d>import std.stdio, std.string, std.algorithm, std.path, std.array;
<syntaxhighlight lang="d">import std.stdio, std.string, std.algorithm, std.path, std.array;


string commonDirPath(in string[] paths, in string sep = "/") pure {
string commonDirPath(in string[] paths, in string sep = "/") pure {
Line 570: Line 682:
"/home/user1/tmp/coven/members"];
"/home/user1/tmp/coven/members"];
writeln(`The common path is: "`, paths.commonDirPath, '"');
writeln(`The common path is: "`, paths.commonDirPath, '"');
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>The common path is: "/home/user1/tmp"</pre>
<pre>The common path is: "/home/user1/tmp"</pre>
=={{header|Delphi}}==
=={{header|Delphi}}==
{{libheader| System.SysUtils}}
{{libheader| System.SysUtils}}
<syntaxhighlight lang="delphi">
<lang Delphi>
program Find_common_directory_path;
program Find_common_directory_path;


Line 635: Line 747:
'/home/user1/tmp/coven/members']));
'/home/user1/tmp/coven/members']));
Readln;
Readln;
end.</lang>
end.</syntaxhighlight>
{{out}}
{{out}}
<pre>/home/user1/tmp</pre>
<pre>/home/user1/tmp</pre>

=={{header|EasyLang}}==
{{trans|AWK}}

<syntaxhighlight lang=easylang>
func$ comdir path$[] .
for i = 1 to len path$[1]
c$ = substr path$[1] i 1
for j = 2 to len path$[]
if c$ <> substr path$[j] i 1
break 2
.
.
if c$ = "/"
f = i - 1
.
.
return substr path$[1] 1 f
.
a$[] &= "/home/user1/tmp/coverage/test"
a$[] &= "/home/user1/tmp/covert/operator"
a$[] &= "/home/user1/tmp/coven/members"
print comdir a$[]
</syntaxhighlight>


=={{header|Elixir}}==
=={{header|Elixir}}==
{{trans|Ruby}}
{{trans|Ruby}}
<lang elixir>defmodule RC do
<syntaxhighlight lang="elixir">defmodule RC do
def common_directory_path(dirs, separator \\ "/") do
def common_directory_path(dirs, separator \\ "/") do
dir1 = Enum.min(dirs) |> String.split(separator)
dir1 = Enum.min(dirs) |> String.split(separator)
Line 651: Line 787:


dirs = ~w( /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members )
dirs = ~w( /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members )
IO.inspect RC.common_directory_path(dirs)</lang>
IO.inspect RC.common_directory_path(dirs)</syntaxhighlight>


{{out}}
{{out}}
Line 659: Line 795:


=={{header|Erlang}}==
=={{header|Erlang}}==
<syntaxhighlight lang="erlang">
<lang Erlang>
-module( find_common_directory ).
-module( find_common_directory ).


Line 671: Line 807:


keep_common( Components, Acc ) -> [X || X <- Components, Y <- Acc, X =:= Y].
keep_common( Components, Acc ) -> [X || X <- Components, Y <- Acc, X =:= Y].
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 679: Line 815:


=={{header|F_Sharp|F#}}==
=={{header|F_Sharp|F#}}==
<lang fsharp>open System
<syntaxhighlight lang="fsharp">open System


let (|SeqNode|SeqEmpty|) s =
let (|SeqNode|SeqEmpty|) s =
Line 708: Line 844:


printfn "The common preffix is: %A" (String.Join("/", (commonPrefix args)))
printfn "The common preffix is: %A" (String.Join("/", (commonPrefix args)))
0</lang>
0</syntaxhighlight>
Output for the given task input
Output for the given task input
<pre>The common preffix is: "/home/user1/tmp"</pre>
<pre>The common preffix is: "/home/user1/tmp"</pre>


=={{header|Factor}}==
=={{header|Factor}}==
<lang factor>: take-shorter ( seq1 seq2 -- shorter )
<syntaxhighlight lang="factor">: take-shorter ( seq1 seq2 -- shorter )
[ shorter? ] 2keep ? ;
[ shorter? ] 2keep ? ;


Line 723: Line 859:


: common-prefix ( seq separator -- prefix )
: common-prefix ( seq separator -- prefix )
[ ] swap '[ _ common-prefix-1 ] map-reduce ;</lang>
[ ] swap '[ _ common-prefix-1 ] map-reduce ;</syntaxhighlight>


( scratchpad ) {
( scratchpad ) {
Line 734: Line 870:
=={{header|FreeBASIC}}==
=={{header|FreeBASIC}}==
{{Trans|Visual Basic}}
{{Trans|Visual Basic}}
<lang freebasic>
<syntaxhighlight lang="freebasic">
' compile: fbc.exe -s console cdp.bas
' compile: fbc.exe -s console cdp.bas


Line 799: Line 935:
Print : Print "hit any key to end program"
Print : Print "hit any key to end program"
Sleep
Sleep
End</lang>
End</syntaxhighlight>
{{out}}
{{out}}
<pre>/home/user1/tmp/coverage/test
<pre>/home/user1/tmp/coverage/test
Line 816: Line 952:
/home/user1/tmp/coven/members
/home/user1/tmp/coven/members
/ <- common</pre>
/ <- common</pre>

=={{header|FutureBasic}}==
<syntaxhighlight lang="futurebasic">
include "NSLog.incl"

local fn FindCommonDirectoryPath
CFArrayRef path1 = fn StringComponentsSeparatedByString( @"/home/user1/tmp/coverage/test", @"/" )
CFArrayRef path2 = fn StringComponentsSeparatedByString( @"/home/user1/tmp/covert/operator", @"/" )
CFArrayRef path3 = fn StringComponentsSeparatedByString( @"/home/user1/tmp/coven/members", @"/" )
long i, count = fn ArrayCount( path1 )
CFMutableStringRef mutStr = fn MutableStringWithCapacity( 0 )
for i = 0 to count - 1
if ( fn StringIsEqual( path1[i], path2[i] ) ) and ( fn StringIsEqual( path2[i], path3[i] ) )
MutableStringAppendString( mutStr, fn StringWithFormat( @"%@/\b", path1[i] ) )
else
exit for
end if
next
NSLog( @"%@", mutstr )
end fn

fn FindCommonDirectoryPath

HandleEvents
</syntaxhighlight>
{{output}}
<pre>
/home/user1/tmp/
</pre>


=={{header|Gambas}}==
=={{header|Gambas}}==
<lang gambas>Public Sub Main()
<syntaxhighlight lang="gambas">Public Sub Main()
Dim sFolder As String[] = ["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"]
Dim sFolder As String[] = ["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"]
Dim sSame As String
Dim sSame As String
Line 834: Line 1,000:
Print Mid(sSame, 1, RInStr(sSame, "/") - 1)
Print Mid(sSame, 1, RInStr(sSame, "/") - 1)


End</lang>
End</syntaxhighlight>
Output:
Output:
<pre>
<pre>
Line 845: Line 1,011:
E.g. (<code>/home/user1, /home/user1/foo, /home/user1/bar</code>) should result in <code>/home/user1</code>, not <code>/home</code>.
E.g. (<code>/home/user1, /home/user1/foo, /home/user1/bar</code>) should result in <code>/home/user1</code>, not <code>/home</code>.


<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 923: Line 1,089:
fmt.Println("Common path:", c)
fmt.Println("Common path:", c)
}
}
}</lang>
}</syntaxhighlight>


=={{header|Groovy}}==
=={{header|Groovy}}==
Solution:
Solution:
<lang groovy>def commonPath = { delim, Object[] paths ->
<syntaxhighlight lang="groovy">def commonPath = { delim, Object[] paths ->
def pathParts = paths.collect { it.split(delim) }
def pathParts = paths.collect { it.split(delim) }
pathParts.transpose().inject([match:true, commonParts:[]]) { aggregator, part ->
pathParts.transpose().inject([match:true, commonParts:[]]) { aggregator, part ->
Line 934: Line 1,100:
aggregator
aggregator
}.commonParts.join(delim)
}.commonParts.join(delim)
}</lang>
}</syntaxhighlight>


Test:
Test:
<lang groovy>println commonPath('/',
<syntaxhighlight lang="groovy">println commonPath('/',
'/home/user1/tmp/coverage/test',
'/home/user1/tmp/coverage/test',
'/home/user1/tmp/covert/operator',
'/home/user1/tmp/covert/operator',
Line 946: Line 1,112:
'/home/user1/tmp/covert/test',
'/home/user1/tmp/covert/test',
'/home/user1/tmp/coven/test',
'/home/user1/tmp/coven/test',
'/home/user1/tmp/covers/test')</lang>
'/home/user1/tmp/covers/test')</syntaxhighlight>


Output:
Output:
Line 955: Line 1,121:
{{works with|GW-BASIC}}
{{works with|GW-BASIC}}
{{works with|Chipmunk Basic}}
{{works with|Chipmunk Basic}}
{{works with|QBasic}}


Because most BASICs don't have any sort of parsing functions built in, we have to deal with the entire string (rather than checking one level at a time).
Because most BASICs don't have any sort of parsing functions built in, we have to deal with the entire string (rather than checking one level at a time).
Line 960: Line 1,127:
Note that if the root directory is the common path, this reports the same as no match found (i.e. blank result).
Note that if the root directory is the common path, this reports the same as no match found (i.e. blank result).


<lang qbasic>10 REM All GOTO statements can be replaced with EXIT FOR in newer BASICs.
<syntaxhighlight lang="qbasic">10 REM All GOTO statements can be replaced with EXIT FOR in newer BASICs.


110 X$ = "/home/user1/tmp/coverage/test"
110 X$ = "/home/user1/tmp/coverage/test"
Line 988: Line 1,155:
350 A = L0 - 1
350 A = L0 - 1
360 P$ = LEFT$(X$, A)
360 P$ = LEFT$(X$, A)
370 PRINT "Common path is '"; P$; "'"</lang>
370 PRINT "Common path is '"; P$; "'"</syntaxhighlight>


Output:
Output:
Line 995: Line 1,162:
=={{header|Haskell}}==
=={{header|Haskell}}==


<lang haskell>import Data.List
<syntaxhighlight lang="haskell">import Data.List


-- Return the common prefix of two lists.
-- Return the common prefix of two lists.
Line 1,017: Line 1,184:
"/home/user1/tmp/covert/operator",
"/home/user1/tmp/covert/operator",
"/home/user1/tmp/coven/members"
"/home/user1/tmp/coven/members"
]</lang>
]</syntaxhighlight>


Or perhaps, expressed in applicative terms:
Or, expressed directly in applicative terms:
<lang haskell>import Data.List (transpose, intercalate)
<syntaxhighlight lang="haskell">import Data.List (transpose, intercalate)
import Data.List.Split (splitOn)
import Data.List.Split (splitOn)

import Data.Bool (bool)

------------------ COMMON DIRECTORY PATH -----------------
cdp :: [String] -> String

cdp =
commonDirectoryPath :: [String] -> String
flip
commonDirectoryPath [] = []
(bool .
commonDirectoryPath xs =
intercalate "/" .
intercalate "/" $
fmap head .
takeWhile ((all . (==) . head) <*> tail) . transpose . fmap (splitOn "/"))
head <$> takeWhile ((all . (==) . head) <*> tail) $
([]) <*>
transpose (splitOn "/" <$> xs)

null
--------------------------- TEST -------------------------
main :: IO ()
main :: IO ()
main =
main =
(putStrLn . cdp)
(putStrLn . commonDirectoryPath)
[ "/home/user1/tmp/coverage/test"
[ "/home/user1/tmp/coverage/test"
, "/home/user1/tmp/covert/operator"
, "/home/user1/tmp/covert/operator"
, "/home/user1/tmp/coven/members"
, "/home/user1/tmp/coven/members"
]</lang>
]</syntaxhighlight>
{{Out}}
{{Out}}
<pre>/home/user1/tmp</pre>
<pre>/home/user1/tmp</pre>


=={{header|HicEst}}==
=={{header|HicEst}}==
<lang HicEst>CHARACTER a='/home/user1/tmp/coverage/test', b='/home/user1/tmp/covert/operator', c='/home/user1/tmp/coven/members'
<syntaxhighlight lang="hicest">CHARACTER a='/home/user1/tmp/coverage/test', b='/home/user1/tmp/covert/operator', c='/home/user1/tmp/coven/members'


minLength = MIN( LEN(a), LEN(b), LEN(c) )
minLength = MIN( LEN(a), LEN(b), LEN(c) )
Line 1,058: Line 1,225:
WRITE(Messagebox, Name) "No common directory for", a, b, c
WRITE(Messagebox, Name) "No common directory for", a, b, c
ENDIF
ENDIF
ENDDO</lang>
ENDDO</syntaxhighlight>


=={{header|Icon}} and {{header|Unicon}}==
=={{header|Icon}} and {{header|Unicon}}==
<lang Icon>procedure main()
<syntaxhighlight lang="icon">procedure main()
write(lcdsubstr(["/home/user1/tmp/coverage/test","/home/user1/tmp/covert/operator","/home/user1/tmp/coven/members"]))
write(lcdsubstr(["/home/user1/tmp/coverage/test","/home/user1/tmp/covert/operator","/home/user1/tmp/coven/members"]))
end
end
Line 1,080: Line 1,247:
if not match(s,x) then fail
if not match(s,x) then fail
return s
return s
end</lang>
end</syntaxhighlight>


=={{header|J}}==
=={{header|J}}==
'''Solution:'''
'''Solution:'''
<lang j>parseDirs =: = <;.2 ]
<syntaxhighlight lang="j">parseDirs =: = <;.2 ]
getCommonPrefix =: {. ;@{.~ 0 i.~ *./@(="1 {.)
getCommonPrefix =: {. ;@{.~ 0 i.~ *./@(="1 {.)


getCommonDirPath=: [: getCommonPrefix parseDirs&></lang>
getCommonDirPath=: [: getCommonPrefix parseDirs&></syntaxhighlight>


'''Example:'''
'''Example:'''
<lang j> paths=: '/home/user1/tmp/coverage/test';'/home/user1/tmp/covert/operator';'/home/user1/tmp/coven/members'
<syntaxhighlight lang="j"> paths=: '/home/user1/tmp/coverage/test';'/home/user1/tmp/covert/operator';'/home/user1/tmp/coven/members'
getCommonPrefix >paths
getCommonPrefix >paths
/home/user1/tmp/cove
/home/user1/tmp/cove
'/' getCommonDirPath paths
'/' getCommonDirPath paths
/home/user1/tmp/</lang>
/home/user1/tmp/</syntaxhighlight>


'''Note:'''
'''Note:'''
This alternative formulation of parseDirs provides cross-platform support, without the need to specify the path separator.
This alternative formulation of parseDirs provides cross-platform support, without the need to specify the path separator.
<lang j>parseDirs =: (PATHSEP_j_&= <;.2 ])@jhostpath</lang>
<syntaxhighlight lang="j">parseDirs =: (PATHSEP_j_&= <;.2 ])@jhostpath</syntaxhighlight>


=={{header|Java}}==
=={{header|Java}}==
{{works with|Java|1.5+}}
{{works with|Java|1.5+}}
This example is case-sensitive.
This example is case-sensitive.
<lang java5>public class CommonPath {
<syntaxhighlight lang="java5">public class CommonPath {
public static String commonPath(String... paths){
public static String commonPath(String... paths){
String commonPath = "";
String commonPath = "";
Line 1,141: Line 1,308:
System.out.println(commonPath(paths2));
System.out.println(commonPath(paths2));
}
}
}</lang>
}</syntaxhighlight>
Output:
Output:
<pre>/home/user1/tmp/
<pre>/home/user1/tmp/
Line 1,148: Line 1,315:


A slightly modified version of the previous program, only the method commonPath() is changed.
A slightly modified version of the previous program, only the method commonPath() is changed.
<lang java5>
<syntaxhighlight lang="java5">
static String commonPath(String... paths){
static String commonPath(String... paths){
String commonPath = "";
String commonPath = "";
Line 1,167: Line 1,334:
return commonPath;
return commonPath;
}
}
</syntaxhighlight>
</lang>


=={{header|JavaScript}}==
=={{header|JavaScript}}==
<lang javascript>
<syntaxhighlight lang="javascript">
/**
/**
* Given an array of strings, return an array of arrays, containing the
* Given an array of strings, return an array of arrays, containing the
Line 1,216: Line 1,383:


console.log(`Common path is: ${commonPath(cdpInput)}`);
console.log(`Common path is: ${commonPath(cdpInput)}`);
</syntaxhighlight>
</lang>


{{out}}
{{out}}
Line 1,224: Line 1,391:


=={{header|jq}}==
=={{header|jq}}==
<lang jq># maximal_initial_subarray takes as input an array of arrays:
<syntaxhighlight lang="jq"># maximal_initial_subarray takes as input an array of arrays:
def maximal_initial_subarray:
def maximal_initial_subarray:
(map( .[0] ) | unique) as $u
(map( .[0] ) | unique) as $u
Line 1,237: Line 1,404:
[.[] | split(slash)] | maximal_initial_subarray | join(slash) ;
[.[] | split(slash)] | maximal_initial_subarray | join(slash) ;


common_path("/")</lang>
common_path("/")</syntaxhighlight>


Assuming the above jq program is in a file named common_path.jq and that the file directories.txt contains the three given directory strings quoted with double quotation marks:
Assuming the above jq program is in a file named common_path.jq and that the file directories.txt contains the three given directory strings quoted with double quotation marks:
<lang jq>$ jq -s -f common_path.jq directories.txt
<syntaxhighlight lang="jq">$ jq -s -f common_path.jq directories.txt
"home/user1/tmp"</lang>
"home/user1/tmp"</syntaxhighlight>


=={{header|Julia}}==
=={{header|Julia}}==
{{works with|Julia|0.6}}
{{works with|Julia|0.6}}


<lang julia>function commonpath(ds::Vector{<:AbstractString}, dlm::Char='/')
<syntaxhighlight lang="julia">function commonpath(ds::Vector{<:AbstractString}, dlm::Char='/')
0 < length(ds) || return ""
0 < length(ds) || return ""
1 < length(ds) || return String(ds[1])
1 < length(ds) || return String(ds[1])
Line 1,265: Line 1,432:


println("Comparing:\n - ", join(test, "\n - "))
println("Comparing:\n - ", join(test, "\n - "))
println("for their common directory path yields:\n", commonpath(test))</lang>
println("for their common directory path yields:\n", commonpath(test))</syntaxhighlight>


{{out}}
{{out}}
Line 1,276: Line 1,443:


=={{header|Kotlin}}==
=={{header|Kotlin}}==
<lang scala>// version 1.1.51
<syntaxhighlight lang="scala">// version 1.1.51


fun findCommonDirPath(paths: List<String>, separator: Char): String {
fun findCommonDirPath(paths: List<String>, separator: Char): String {
Line 1,303: Line 1,470:
println("The common directory path of:\n\n$pathsToPrint\n")
println("The common directory path of:\n\n$pathsToPrint\n")
println("is '${findCommonDirPath(paths, '/')}'")
println("is '${findCommonDirPath(paths, '/')}'")
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 1,317: Line 1,484:


=={{header|Lasso}}==
=={{header|Lasso}}==
<lang Lasso>#!/usr/bin/lasso9
<syntaxhighlight lang="lasso">#!/usr/bin/lasso9


local(
local(
Line 1,333: Line 1,500:
}
}


stdoutnl(commonpath(#path1, #path2, #path3))</lang>
stdoutnl(commonpath(#path1, #path2, #path3))</syntaxhighlight>


Output:
Output:
Line 1,339: Line 1,506:


=={{header|Liberty BASIC}}==
=={{header|Liberty BASIC}}==
<lang lb>path$(1) = "/home/user1/tmp/coverage/test"
<syntaxhighlight lang="lb">path$(1) = "/home/user1/tmp/coverage/test"
path$(2) = "/home/user1/tmp/covert/operator"
path$(2) = "/home/user1/tmp/covert/operator"
path$(3) = "/home/user1/tmp/coven/members"
path$(3) = "/home/user1/tmp/coven/members"
Line 1,363: Line 1,530:
end if
end if
wend
wend
end function</lang>
end function</syntaxhighlight>


=={{header|Lingo}}==
=={{header|Lingo}}==
<lang lingo>on getCommonPath (pathes, sep)
<syntaxhighlight lang="lingo">on getCommonPath (pathes, sep)
_player.itemDelimiter = sep
_player.itemDelimiter = sep


Line 1,384: Line 1,551:
end repeat
end repeat
return pathes[1].item[1..commonCnt]
return pathes[1].item[1..commonCnt]
end</lang>
end</syntaxhighlight>
<lang lingo>pathes = []
<syntaxhighlight lang="lingo">pathes = []
pathes.add("/home/user1/tmp/coverage/test")
pathes.add("/home/user1/tmp/coverage/test")
pathes.add("/home/user1/tmp/covert/operator")
pathes.add("/home/user1/tmp/covert/operator")
Line 1,391: Line 1,558:


put getCommonPath(pathes, "/")
put getCommonPath(pathes, "/")
-- "/home/user1/tmp"</lang>
-- "/home/user1/tmp"</syntaxhighlight>


=={{header|MapBasic}}==
=={{header|MapBasic}}==
Line 1,397: Line 1,564:
Derived from the [https://www.rosettacode.org/wiki/Find_common_directory_path#BASIC BASIC] example above
Derived from the [https://www.rosettacode.org/wiki/Find_common_directory_path#BASIC BASIC] example above


<lang qbasic>Include "MapBasic.def"
<syntaxhighlight lang="qbasic">Include "MapBasic.def"


Declare Sub Main
Declare Sub Main
Line 1,462: Line 1,629:
PRINT "Common path is " + commonPath(x(), Sep)
PRINT "Common path is " + commonPath(x(), Sep)


End Sub</lang>
End Sub</syntaxhighlight>


=={{header|Maple}}==
=={{header|Maple}}==
<syntaxhighlight lang="maple">
<lang Maple>
dirpath:=proc(a,b,c)
dirpath:=proc(a,b,c)
local dirtemp,dirnew,x;
local dirtemp,dirnew,x;
Line 1,475: Line 1,642:
end use;
end use;
end proc;
end proc;
</syntaxhighlight>
</lang>


=={{header|Mathematica}}==
=={{header|Mathematica}}/{{header|Wolfram Language}}==
<lang Mathematica>FindCommonDirectory[x_] := If[StringTake[#, -1] != "/", StringTake[#, Max[StringPosition[#, "/"]]], #] &
<syntaxhighlight lang="mathematica">FindCommonDirectory[x_] := If[StringTake[#, -1] != "/", StringTake[#, Max[StringPosition[#, "/"]]], #] &
[Fold[LongestCommonSubsequence, First[x] , Rest[x]]]
[Fold[LongestCommonSubsequence, First[x] , Rest[x]]]


FindCommonDirectory[{"/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"}]
FindCommonDirectory[{"/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"}]
->"/home/user1/tmp/"</lang>
->"/home/user1/tmp/"</syntaxhighlight>


=={{header|MATLAB}} / {{header|Octave}}==
=={{header|MATLAB}} / {{header|Octave}}==
<syntaxhighlight lang="matlab">
<lang Matlab>
function lcp = longest_common_dirpath(varargin)
function lcp = longest_common_dirpath(varargin)
ix = find(varargin{1}=='/');
ix = find(varargin{1}=='/');
Line 1,497: Line 1,664:


longest_common_dirpath('/home/user1/tmp/coverage/test', '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members')
longest_common_dirpath('/home/user1/tmp/coverage/test', '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members')
</syntaxhighlight>
</lang>


{{out}}
{{out}}
Line 1,505: Line 1,672:


=={{header|Maxima}}==
=={{header|Maxima}}==
<lang maxima>scommon(a, b) := block([n: min(slength(a), slength(b))],
<syntaxhighlight lang="maxima">scommon(a, b) := block([n: min(slength(a), slength(b))],
substring(a, 1, catch(for i thru n do (
substring(a, 1, catch(for i thru n do (
if not cequal(charat(a, i), charat(b, i)) then throw(i)), n + 1)))$
if not cequal(charat(a, i), charat(b, i)) then throw(i)), n + 1)))$
Line 1,515: Line 1,682:


commonpath(["c:/files/banister.jpg", "c:/files/bank.xls", "c:/files/banana-recipes.txt"]);
commonpath(["c:/files/banister.jpg", "c:/files/bank.xls", "c:/files/banana-recipes.txt"]);
"c:/files"</lang>
"c:/files"</syntaxhighlight>


=={{header|MUMPS}}==
=={{header|MUMPS}}==
<syntaxhighlight lang="mumps">FCD
<lang MUMPS>FCD
NEW D,SEP,EQ,LONG,DONE,I,J,K,RETURN
NEW D,SEP,EQ,LONG,DONE,I,J,K,RETURN
SET D(1)="/home/user1/tmp/coverage/test"
SET D(1)="/home/user1/tmp/coverage/test"
Line 1,532: Line 1,699:
WRITE !,"The longest common directory is: ",RETURN
WRITE !,"The longest common directory is: ",RETURN
KILL D,SEP,EQ,LONG,DONE,I,J,K,RETURN
KILL D,SEP,EQ,LONG,DONE,I,J,K,RETURN
QUIT</lang>
QUIT</syntaxhighlight>
Usage:<pre>
Usage:<pre>
USER>D FCD^ROSETTA
USER>D FCD^ROSETTA
Line 1,543: Line 1,710:


=={{header|Nim}}==
=={{header|Nim}}==
<lang nim>import strutils
<syntaxhighlight lang="nim">import strutils


proc commonprefix(paths: openarray[string], sep = "/"): string =
proc commonprefix(paths: openarray[string], sep = "/"): string =
Line 1,555: Line 1,722:
result = result[0 .. result.rfind(sep)]
result = result[0 .. result.rfind(sep)]


echo commonprefix(@["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"])</lang>
echo commonprefix(@["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"])</syntaxhighlight>
Output:
Output:
<pre>/home/user1/tmp</pre>
<pre>/home/user1/tmp</pre>


=={{header|OCaml}}==
=={{header|OCaml}}==
<syntaxhighlight lang="ocaml">let rec common_prefix xs ys = match xs, ys with
| x :: xs, y :: ys when x = y -> x :: common_prefix xs ys
| _ -> []


let common_prefix_all = function
<lang ocaml>let rec aux acc paths =
| x :: xs -> List.fold_left common_prefix x xs
if List.mem [] paths
| _ -> []
then (List.rev acc) else
let heads = List.map List.hd paths in
let item = List.hd heads in
let all_the_same =
List.for_all ((=) item) (List.tl heads)
in
if all_the_same
then aux (item::acc) (List.map List.tl paths)
else (List.rev acc)


let common_prefix sep = function
let common_ancestor ~sep paths =
List.map Str.(split_delim (regexp_string sep)) paths
| [] -> invalid_arg "common_prefix"
|> common_prefix_all
| dirs ->
|> String.concat sep
let paths = List.map (Str.split (Str.regexp_string sep)) dirs in
let res = aux [] paths in
(sep ^ (String.concat sep res))


let () =
let _ = assert begin
common_ancestor ~sep:"/" [
let dirs = [
"/home/user1/tmp/coverage/test";
"/home/user1/tmp/coverage/test";
"/home/user1/tmp/covert/operator";
"/home/user1/tmp/covert/operator";
"/home/user1/tmp/coven/members";
"/home/user1/tmp/coven/members";
] = "/home/user1/tmp"
] in
end</syntaxhighlight>
print_endline (common_prefix "/" dirs);
;;</lang>


(uses the module <code>[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Str.html Str]</code>, str.cma)
Requires the standard <code>[https://ocaml.org/manual/libstr.html str]</code> library


=={{header|OpenEdge/Progress}}==
=={{header|OpenEdge/Progress}}==
<lang progress>FUNCTION findCommonDir RETURNS CHAR(
<syntaxhighlight lang="progress">FUNCTION findCommonDir RETURNS CHAR(
i_cdirs AS CHAR,
i_cdirs AS CHAR,
i_cseparator AS CHAR
i_cseparator AS CHAR
Line 1,622: Line 1,781:
RETURN cresult.
RETURN cresult.


END FUNCTION.</lang>
END FUNCTION.</syntaxhighlight>


<lang progress>MESSAGE
<syntaxhighlight lang="progress">MESSAGE
findCommonDir(
findCommonDir(
'/home/user1/tmp/coverage/test' + '~n' +
'/home/user1/tmp/coverage/test' + '~n' +
Line 1,631: Line 1,790:
'/'
'/'
)
)
VIEW-AS ALERT-BOX</lang>
VIEW-AS ALERT-BOX</syntaxhighlight>


Output
Output
Line 1,645: Line 1,804:
=={{header|Oz}}==
=={{header|Oz}}==
With a few helper functions, we can express the solution like this in Oz:
With a few helper functions, we can express the solution like this in Oz:
<lang oz>declare
<syntaxhighlight lang="oz">declare
fun {CommonPrefix Sep Paths}
fun {CommonPrefix Sep Paths}
fun {GetParts P} {String.tokens P Sep} end
fun {GetParts P} {String.tokens P Sep} end
Line 1,676: Line 1,835:
["/home/user1/tmp/coverage/test"
["/home/user1/tmp/coverage/test"
"/home/user1/tmp/covert/operator"
"/home/user1/tmp/covert/operator"
"/home/user1/tmp/coven/members"]}}</lang>
"/home/user1/tmp/coven/members"]}}</syntaxhighlight>


=={{header|PARI/GP}}==
=={{header|PARI/GP}}==
<lang parigp>cdp(v)={
<syntaxhighlight lang="parigp">cdp(v)={
my(s="");
my(s="");
v=apply(t->Vec(t),v);
v=apply(t->Vec(t),v);
Line 1,690: Line 1,849:
if(vecmax(apply(length,v))==vecmin(apply(length,v)),concat(v[1]),s)
if(vecmax(apply(length,v))==vecmin(apply(length,v)),concat(v[1]),s)
};
};
cdp(["/home/user1/tmp/coverage/test","/home/user1/tmp/covert/operator","/home/user1/tmp/coven/members"])</lang>
cdp(["/home/user1/tmp/coverage/test","/home/user1/tmp/covert/operator","/home/user1/tmp/coven/members"])</syntaxhighlight>

=={{header|Pascal}}==
==={{header|Free Pascal}}===
<syntaxhighlight lang="pascal">
Program CommonPaths;
{$mode ObjFPC}{$H+}
uses
Classes, Math;

const
Paths: array of string = ('/home/user1/tmp/coverage/test',
'/home/user1/tmp/covert/operator',
'/home/user1/tmp/coven/members');

function FindShortestCommonPath(arr: array of TStringList; shortestPath: Integer): string;
var
i, j: Integer;
commonStr: string;
begin
Result := '/';
if Length(arr) = 0 then
Exit;
for j := 0 to shortestPath - 1 do
begin
commonStr := arr[0][j];
for i := 1 to High(arr) do
begin
if arr[i][j] <> commonStr then
Exit(Result);
end;
Result := Result + commonStr + '/';
end;
end;
var
arr: array of TStringList;
i, shortestpath: uint32;

begin
shortestpath := High(uint32);
SetLength(arr, Length(paths));

for i := 0 to High(paths) do
begin
arr[i] := TStringList.Create;
arr[i].AddDelimitedText(paths[i], '/', false);
arr[i].Delete(0);
shortestpath := Min(shortestpath, arr[i].Count);
end;

Writeln(FindShortestCommonPath(arr, shortestpath));

for i := 0 to High(paths) do
arr[i].Free;
end.
</syntaxhighlight>
{{out}}
<pre>
/home/user1/tmp/
</pre>





=={{header|Perl}}==
=={{header|Perl}}==
Line 1,696: Line 1,917:
A solution which lets the regex engine do all the work ''(it operates on the concatenation of the given paths delimited by null-bytes, which should be safe since null-bytes are not allowed inside paths)'':
A solution which lets the regex engine do all the work ''(it operates on the concatenation of the given paths delimited by null-bytes, which should be safe since null-bytes are not allowed inside paths)'':


<lang Perl>sub common_prefix {
<syntaxhighlight lang="perl">sub common_prefix {
my $sep = shift;
my $sep = shift;
my $paths = join "\0", map { $_.$sep } @_;
my $paths = join "\0", map { $_.$sep } @_;
$paths =~ /^ ( [^\0]* ) $sep [^\0]* (?: \0 \1 $sep [^\0]* )* $/x;
$paths =~ /^ ( [^\0]* ) $sep [^\0]* (?: \0 \1 $sep [^\0]* )* $/x;
return $1;
return $1;
}</lang>
}</syntaxhighlight>


A more conventional solution, which tallies up all potential prefixes from the given paths and then looks for the longest one that occurred the same number of times as there are paths:
A more conventional solution, which tallies up all potential prefixes from the given paths and then looks for the longest one that occurred the same number of times as there are paths:


<lang Perl>use List::Util qw(first);
<syntaxhighlight lang="perl">use List::Util qw(first);


sub common_prefix {
sub common_prefix {
Line 1,716: Line 1,937:
return first { $prefixes{$_} == @paths } reverse sort keys %prefixes;
return first { $prefixes{$_} == @paths } reverse sort keys %prefixes;
}</lang>
}</syntaxhighlight>


'''Testing:'''
'''Testing:'''


<lang perl>my @paths = qw(/home/user1/tmp/coverage/test
<syntaxhighlight lang="perl">my @paths = qw(/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members);
/home/user1/tmp/coven/members);
print common_prefix('/', @paths), "\n";</lang>
print common_prefix('/', @paths), "\n";</syntaxhighlight>


{{out}}
{{out}}
Line 1,731: Line 1,952:
Note: if the testset contains /home/user1/tmp the result is /home/user1, with /home/user1/tmp/ instead of that, it is /home/user1/tmp<br>
Note: if the testset contains /home/user1/tmp the result is /home/user1, with /home/user1/tmp/ instead of that, it is /home/user1/tmp<br>
To change that behaviour, simply remove both the [1..-2]<br>
To change that behaviour, simply remove both the [1..-2]<br>
For cross-platform operation, simply use the split_path and join_path builtins instead of split and join.
For cross-platform operation, simply use the split_path and join_path builtins (not pwa/p2js compatible) instead of split and join.
<!--<syntaxhighlight lang="phix">(phixonline)-->
<lang Phix>function common_directory_path(sequence paths, integer sep='/')
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
sequence res = {}
<span style="color: #008080;">function</span> <span style="color: #000000;">common_directory_path</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">paths</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">sep</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'/'</span><span style="color: #0000FF;">)</span>
if length(paths) then
<span style="color: #004080;">sequence</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
res = split(paths[1],sep)[1..-2]
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">paths</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
for i=2 to length(paths) do
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">paths</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">sep</span><span style="color: #0000FF;">)[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]</span>
sequence pi = split(paths[i],sep)[1..-2]
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">paths</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
for j=1 to length(res) do
<span style="color: #004080;">sequence</span> <span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">paths</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">sep</span><span style="color: #0000FF;">)[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]</span>
if j>length(pi) or res[j]!=pi[j] then
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
res = res[1..j-1]
<span style="color: #008080;">if</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">></span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">or</span> <span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]!=</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">then</span>
exit
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">j</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
end if
<span style="color: #008080;">exit</span>
end for
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
if length(res)=0 then exit end if
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end for
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end if
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
return join(res,sep)
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end function
<span style="color: #008080;">return</span> <span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #000000;">sep</span><span style="color: #0000FF;">)</span>

<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
constant test = {"/home/user1/tmp/coverage/test",
"/home/user1/tmp/covert/operator",
<span style="color: #008080;">constant</span> <span style="color: #000000;">test</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"/home/user1/tmp/coverage/test"</span><span style="color: #0000FF;">,</span>
"/home/user1/tmp/coven/members"}
<span style="color: #008000;">"/home/user1/tmp/covert/operator"</span><span style="color: #0000FF;">,</span>
?common_directory_path(test)</lang>
<span style="color: #008000;">"/home/user1/tmp/coven/members"</span><span style="color: #0000FF;">}</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">common_directory_path</span><span style="color: #0000FF;">(</span><span style="color: #000000;">test</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 1,760: Line 1,984:


=={{header|PHP}}==
=={{header|PHP}}==
<lang php><?php
<syntaxhighlight lang="php"><?php


/*
/*
Line 1,816: Line 2,040:
}
}


?></lang>
?></syntaxhighlight>




<lang php><?php
<syntaxhighlight lang="php"><?php


/* A more compact string-only version, which I assume would be much faster */
/* A more compact string-only version, which I assume would be much faster */
Line 1,840: Line 2,064:
}
}


?></lang>
?></syntaxhighlight>

=={{header|Picat}}==
Here are two different approaches. Both use <code>append/3</code> for getting the common prefix.

===Using maxof/2===
Using <code>maxof/2</code> to get the longest common prefix.
<syntaxhighlight lang="picat">find_common_directory_path(Dirs) = Path =>
maxof( (common_prefix(Dirs, Path,Len), append(_,"/",Path)), Len).

%
% Find a common prefix of all lists/strings in Ls.
% Using append/3.
%
common_prefix(Ls, Prefix,Len) =>
foreach(L in Ls)
append(Prefix,_,L)
end,
Len = Prefix.length.</syntaxhighlight>

===Mode-directed tabling===
Using mode-directed tabling for maximizing the length.
<syntaxhighlight lang="picat">find_common_directory_path2(Dirs) = Path =>
common_prefix2(Dirs,Path,'/',_Len).

table(+,-,max)
common_prefix2(Ls,Prefix,Len) =>
common_prefix2(Ls,Prefix,[],Len).

table(+,-,+,max)
common_prefix2(Ls,Prefix,Last,Len) =>
foreach(L in Ls)
append(Prefix,_,L)
end,
if Last != [], Prefix != [] then
Prefix.last() == Last
end,
Len = Prefix.length.</syntaxhighlight>

For the directories
<pre>
Dirs = [
"/home/user1/tmp/coverage/test",
"/home/user1/tmp/covert/operator",
"/home/user1/tmp/coven/members"
]
</pre>

both find this solution:
<pre>path = /home/user1/tmp/</pre>


=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
<lang PicoLisp>(de commonPath (Lst Chr)
<syntaxhighlight lang="picolisp">(de commonPath (Lst Chr)
(glue Chr
(glue Chr
(make
(make
(apply find
(apply find
(mapcar '((L) (split (chop L) Chr)) Lst)
(mapcar '((L) (split (chop L) Chr)) Lst)
'(@ (or (pass <>) (nil (link (next))))) ) ) ) )</lang>
'(@ (or (pass <>) (nil (link (next))))) ) ) ) )</syntaxhighlight>
Output:
Output:
<pre>(commonPath
<pre>(commonPath
Line 1,860: Line 2,133:


=={{header|Pike}}==
=={{header|Pike}}==
<lang Pike>array paths = ({ "/home/user1/tmp/coverage/test",
<syntaxhighlight lang="pike">array paths = ({ "/home/user1/tmp/coverage/test",
"/home/user1/tmp/covert/operator",
"/home/user1/tmp/covert/operator",
"/home/user1/tmp/coven/members" });
"/home/user1/tmp/coven/members" });
Line 1,870: Line 2,143:
string cp = String.common_prefix(paths);
string cp = String.common_prefix(paths);
cp = cp[..sizeof(cp)-search(reverse(cp), "/")-2];
cp = cp[..sizeof(cp)-search(reverse(cp), "/")-2];
Result: "/home/user1/tmp"</lang>
Result: "/home/user1/tmp"</syntaxhighlight>


=={{header|PowerBASIC}}==
=={{header|PowerBASIC}}==
{{Trans|Visual Basic}}
{{Trans|Visual Basic}}
<lang powerbasic>#COMPILE EXE
<syntaxhighlight lang="powerbasic">#COMPILE EXE
#DIM ALL
#DIM ALL
#COMPILER PBCC 6
#COMPILER PBCC 6
Line 1,941: Line 2,214:
CON.WAITKEY$
CON.WAITKEY$


END FUNCTION</lang>
END FUNCTION</syntaxhighlight>
{{out}}
{{out}}
<pre>/home/user1/tmp/coverage/test
<pre>/home/user1/tmp/coverage/test
Line 1,961: Line 2,234:
=={{header|PowerShell}}==
=={{header|PowerShell}}==


<syntaxhighlight lang="powershell">
<lang Powershell>
<#
<#
.Synopsis
.Synopsis
Line 1,989: Line 2,262:
}
}
}
}
</syntaxhighlight>
</lang>


Sample execution:
Sample execution:
<lang>
<syntaxhighlight lang="text">
"C:\a\b\c\d\e","C:\a\b\e\f","C:\a\b\c\d\x" | Get-CommonPath
"C:\a\b\c\d\e","C:\a\b\e\f","C:\a\b\c\d\x" | Get-CommonPath
C:\a\b
C:\a\b
</syntaxhighlight>
</lang>

=={{header|Prolog}}==

<syntaxhighlight lang="prolog">

%! directory_prefix(PATHs,STOP0,PREFIX)

directory_prefix([],_STOP0_,'')
:-
!
.

directory_prefix(PATHs0,STOP0,PREFIX)
:-
prolog:once(longest_prefix(PATHs0,STOP0,LONGEST_PREFIX)) ->
prolog:atom_concat(PREFIX,STOP0,LONGEST_PREFIX) ;
PREFIX=''
.

%! longest_prefix(PATHs,STOP0,PREFIX)

longest_prefix(PATHs0,STOP0,PREFIX)
:-
QUERY=(shortest_prefix(PATHs0,STOP0,SHORTEST_PREFIX)) ,
prolog:findall(SHORTEST_PREFIX,QUERY,SHORTEST_PREFIXs) ,
lists:reverse(SHORTEST_PREFIXs,LONGEST_PREFIXs) ,
lists:member(PREFIX,LONGEST_PREFIXs)
.

%! shortest_prefix(PATHs,STOP0,PREFIX)

shortest_prefix([],_STOP0_,_PREFIX_) .

shortest_prefix([PATH0|PATHs0],STOP0,PREFIX)
:-
starts_with(PATH0,PREFIX) ,
ends_with(PREFIX,STOP0) ,
shortest_prefix(PATHs0,STOP0,PREFIX)
.

%! starts_with(TARGET,START)

starts_with(TARGET,START)
:-
prolog:atom_concat(START,_,TARGET)
.

%! ends_with(TARGET,END)

ends_with(TARGET,END)
:-
prolog:atom_concat(_,END,TARGET)
.

</syntaxhighlight>

{{out}}
<pre>
?- directory_prefix(['/home/user1/tmp/coverage/test','/home/user1/tmp/convert/operator','/home/user1/tmp/coven/members'],'/',PREFIX) .
PREFIX = '/home/user1/tmp' .
</pre>


=={{header|PureBasic}}==
=={{header|PureBasic}}==
Line 2,002: Line 2,336:


Simply by checking the catalog names until they mismatch and add up the correct parts, the task is accomplished.
Simply by checking the catalog names until they mismatch and add up the correct parts, the task is accomplished.
<lang PureBasic>Procedure.s CommonPath(Array InPaths.s(1),separator.s="/")
<syntaxhighlight lang="purebasic">Procedure.s CommonPath(Array InPaths.s(1),separator.s="/")
Protected SOut$=""
Protected SOut$=""
Protected i, j, toggle
Protected i, j, toggle
Line 2,024: Line 2,358:
Next
Next
ForEver
ForEver
EndProcedure</lang>
EndProcedure</syntaxhighlight>


Example of implementation
Example of implementation
<lang PureBasic>Dim t.s(2)
<syntaxhighlight lang="purebasic">Dim t.s(2)
t(0)="/home/user1/tmp/coverage/test"
t(0)="/home/user1/tmp/coverage/test"
t(1)="/home/user1/tmp/covert/operator"
t(1)="/home/user1/tmp/covert/operator"
t(2)="/home/user1/tmp/coven/members"
t(2)="/home/user1/tmp/coven/members"


Debug CommonPath(t(),"/"))</lang>
Debug CommonPath(t(),"/"))</syntaxhighlight>


=={{header|Python}}==
=={{header|Python}}==

Since Python 3.5 os.path.commonpath function can be used:
<syntaxhighlight lang="python">>>> import os
>>> os.path.commonpath(['/home/user1/tmp/coverage/test',
'/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp'</syntaxhighlight>

The Python os.path.commonprefix function is [http://nedbatchelder.com/blog/201003/whats_the_point_of_ospathcommonprefix.html broken] as it returns common characters that may not form a valid directory path:
The Python os.path.commonprefix function is [http://nedbatchelder.com/blog/201003/whats_the_point_of_ospathcommonprefix.html broken] as it returns common characters that may not form a valid directory path:
<lang python>>>> import os
<syntaxhighlight lang="python">>>> import os
>>> os.path.commonprefix(['/home/user1/tmp/coverage/test',
>>> os.path.commonprefix(['/home/user1/tmp/coverage/test',
'/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp/cove'</lang>
'/home/user1/tmp/cove'</syntaxhighlight>


This result can be fixed:
This result can be fixed:
<lang python>>>> def commonprefix(args, sep='/'):
<syntaxhighlight lang="python">>>> def commonprefix(args, sep='/'):
return os.path.commonprefix(args).rpartition(sep)[0]
return os.path.commonprefix(args).rpartition(sep)[0]


>>> commonprefix(['/home/user1/tmp/coverage/test',
>>> commonprefix(['/home/user1/tmp/coverage/test',
'/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp'</lang>
'/home/user1/tmp'</syntaxhighlight>


Even shorter:
Even shorter:
<lang python>>>> paths = ['/home/user1/tmp/coverage/test', '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members']
<syntaxhighlight lang="python">>>> paths = ['/home/user1/tmp/coverage/test', '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members']
>>> os.path.dirname(os.path.commonprefix(paths))
>>> os.path.dirname(os.path.commonprefix(paths))
'/home/user1/tmp'</lang>
'/home/user1/tmp'</syntaxhighlight>


But it may be better to not rely on the faulty implementation at all:
But it may be better to not rely on the faulty implementation at all:
<lang python>>>> from itertools import takewhile
<syntaxhighlight lang="python">>>> from itertools import takewhile
>>> def allnamesequal(name):
>>> def allnamesequal(name):
return all(n==name[0] for n in name[1:])
return all(n==name[0] for n in name[1:])
Line 2,070: Line 2,411:
'/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp'
'/home/user1/tmp'
>>> </lang>
>>> </syntaxhighlight>

=={{header|Quackery}}==

<syntaxhighlight lang="Quackery"> [ over size
over size min
dup dip unrot times
[ over i^ peek
over i^ peek
!= if
[ rot drop
i^ unrot
conclude ] ]
2drop ] is equalto ( $ $ --> n )

[ 0 unrot over size
times
[ over i peek
over = if
[ rot drop
i unrot
conclude ] ]
2drop ] is lastof ( $ c --> n )

[ swap behead swap
witheach
[ over equalto
split drop ]
dup rot lastof
split drop ] is cdp ( [ c --> n )

$ '/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members' nest$
char / cdp echo$</syntaxhighlight>

{{out}}

<pre>/home/user1/tmp</pre>


=={{header|R}}==
=={{header|R}}==
<syntaxhighlight lang="r">
<lang r>
get_common_dir <- function(paths, delim = "/")
get_common_dir <- function(paths, delim = "/")
{
{
Line 2,096: Line 2,475:
get_common_dir(paths) # "/home/user1/tmp"
get_common_dir(paths) # "/home/user1/tmp"


</syntaxhighlight>
</lang>


=={{header|Racket}}==
=={{header|Racket}}==
<syntaxhighlight lang="racket">
<lang Racket>
#lang racket
#lang racket


Line 2,118: Line 2,497:
"/home/user1/tmp/coven/members")
"/home/user1/tmp/coven/members")
;; --> "/home/user1/tmp"
;; --> "/home/user1/tmp"
</syntaxhighlight>
</lang>


=={{header|Raku}}==
=={{header|Raku}}==
(formerly Perl 6)
(formerly Perl 6)
<lang perl6>my $sep = '/';
<syntaxhighlight lang="raku" line>my $sep = '/';
my @dirs = </home/user1/tmp/coverage/test
my @dirs = </home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/covert/operator
Line 2,137: Line 2,516:


say "The longest common path is $prefix";
say "The longest common path is $prefix";
</syntaxhighlight>
</lang>
Output:
Output:
<pre>
<pre>
Line 2,143: Line 2,522:
</pre>
</pre>
If you'd prefer a pure FP solution without side effects, you can use this:
If you'd prefer a pure FP solution without side effects, you can use this:
<lang perl6>my $sep := '/';
<syntaxhighlight lang="raku" line>my $sep := '/';
my @dirs := </home/user1/tmp/coverage/test
my @dirs := </home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/covert/operator
Line 2,154: Line 2,533:
last unless all(@comps[*]»[$column]) eq @comps[0][$column];
last unless all(@comps[*]»[$column]) eq @comps[0][$column];
take @comps[0][$column] // last;
take @comps[0][$column] // last;
}</lang>
}</syntaxhighlight>
Or here's another factoring, that focuses on building the result with cumulative sequences and getting the solution with `first`:
Or here's another factoring, that focuses on building the result with cumulative sequences and getting the solution with `first`:
<lang perl6>my $sep = '/';
<syntaxhighlight lang="raku" line>my $sep = '/';
my @dirs = </home/user1/tmp/coverage/test
my @dirs = </home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/covert/operator
Line 2,163: Line 2,542:
sub is_common_prefix { so $^prefix eq all(@dirs).substr(0, $prefix.chars) }
sub is_common_prefix { so $^prefix eq all(@dirs).substr(0, $prefix.chars) }


say ([\~] @dirs.comb(/ $sep [ <!before $sep> . ]* /)).reverse.first: &is_common_prefix</lang>
say ([\~] @dirs.comb(/ $sep [ <!before $sep> . ]* /)).reverse.first: &is_common_prefix</syntaxhighlight>


=={{header|REXX}}==
=={{header|REXX}}==
<lang rexx>/*REXX program finds the common directory path for a list of files. */
<syntaxhighlight lang="rexx">/*REXX program finds the common directory path for a list of files. */
@. = /*the default for all file lists (null)*/
/* original code: Gerard Schildberger */
/* 20230606 Walter Pachl refurbisher adn improved (file.4 = 'home' -> /) */
@.1 = '/home/user1/tmp/coverage/test'
file. = '' /*the default for all file lists (null)*/
@.2 = '/home/user1/tmp/covert/operator'
@.3 = '/home/user1/tmp/coven/members'
file.1 = '/home/user1/tmp/coverage/test'
/*123456789.123456789.123456768*/
L= length(@.1) /*use the length of the first string. */
file.2 = '/home/user1/tmp/covert/operator'
do j=2 while @.j\=='' /*start search with the second string. */
file.3 = '/home/user1/tmp/coven/members'
_= compare(@.j, @.1) /*use REXX compare BIF for comparison*/
if _==0 then iterate /*Strings are equal? Then con't use min*/
L= min(L, _) /*get the minimum length equal strings.*/
if right(@.j, 1)=='/' then iterate /*if a directory, then it's OK. */
L= lastpos('/', left(@.j, L) ) /*obtain directory name up to here*/
end /*j*/


common= left( @.1, lastpos('/', @.1, L) ) /*determine the shortest DIR string. */
L=length(file.1) /*use the length of the first string. */
Do j=2 While file.j\=='' /*loop for the other file names */
if right(common, 1)=='/' then common= left(common, max(0, length(common) - 1) )
diffp=compare(file.j,file.1) /*find the first different character */
if common=='' then common= "/" /*if no common directory, assume home. */
say 'common directory path: ' common /* [↑] handle trailing / delimiter*/
If diffp>0 Then Do /*Strings are different */
/*stick a fork in it, we're all done. */</lang>
L=min(L,diffp) /*get the minimum length equal strings.*/
If right(file.j,1)<>'/' Then Do /*not a directory */
L=lastpos('/',left(file.j,L)) /* go back to directory end */
If L=0 Then Do
Say 'common directory path: /'
Exit
End
End
End
End
common=left(file.1,lastpos('/',file.1,L)) /*determine the shortest DIR string.*/
If right(common,1)=='/' Then /* remove the trailing / */
common=left(common,length(common)-1)
If common=='' then common= "/" /*if no common directory, assume home. */
Say 'common directory path: 'common
/*stick a fork in it, we're all done. */
</syntaxhighlight>
{{out|output|text=&nbsp; when using the default inputs:}}
{{out|output|text=&nbsp; when using the default inputs:}}
<pre>
<pre>
Line 2,191: Line 2,581:


=={{header|Ring}}==
=={{header|Ring}}==
<lang ring>
<syntaxhighlight lang="ring">
# Project : Find common directory path
# Project : Find common directory path


Line 2,216: Line 2,606:
end
end
return left(p[1], o-1)
return left(p[1], o-1)
</syntaxhighlight>
</lang>
Output:
Output:
<pre>
<pre>
/home/user1/tmp
/home/user1/tmp
</pre>

=={{header|RPL}}==
{{works with|HP|48}}
≪ DUP SIZE → paths n
≪ paths n GET "/" +
'''WHILE''' 'n' DECR '''REPEAT'''
paths n GET "/" +
DUP2 SIZE SWAP SIZE MIN DUP
1 SWAP '''FOR''' j
DROP
OVER j DUP SUB OVER j DUP SUB ≠
j
'''IF''' SWAP '''THEN''' DUP 1 ≠ - OVER SIZE 'j' STO '''END'''
'''NEXT'''
1 SWAP SUB SWAP DROP
'''END'''
DUP SIZE
'''WHILE''' DUP2 DUP SUB "/" ≠ '''REPEAT''' 1 - '''END'''
DUP 1 ≠ -
1 SWAP SUB
≫ ≫ '<span style="color:blue">CPATH</span>' STO

{ "/home/user1/tmp/coverage/test" "/home/user1/tmp/covert/operator" "/home/user1/tmp/coven/members" } <span style="color:blue">CPATH</span>
{{out}}
<pre>
1: "/home/user1/tmp"
</pre>
</pre>


Line 2,225: Line 2,642:
Uses the standard library <code>[http://www.ruby-doc.org/stdlib/libdoc/abbrev/rdoc/index.html abbrev]</code> module: Given a set of strings, calculate the set of unambiguous abbreviations for those strings, and return a hash where the keys are all the possible abbreviations and the values are the full strings.
Uses the standard library <code>[http://www.ruby-doc.org/stdlib/libdoc/abbrev/rdoc/index.html abbrev]</code> module: Given a set of strings, calculate the set of unambiguous abbreviations for those strings, and return a hash where the keys are all the possible abbreviations and the values are the full strings.


<lang ruby>require 'abbrev'
<syntaxhighlight lang="ruby">require 'abbrev'


dirs = %w( /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members )
dirs = %w( /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members )


common_prefix = dirs.abbrev.keys.min_by {|key| key.length}.chop # => "/home/user1/tmp/cove"
common_prefix = dirs.abbrev.keys.min_by {|key| key.length}.chop # => "/home/user1/tmp/cove"
common_directory = common_prefix.sub(%r{/[^/]*$}, '') # => "/home/user1/tmp"</lang>
common_directory = common_prefix.sub(%r{/[^/]*$}, '') # => "/home/user1/tmp"</syntaxhighlight>


Implementing without that module:
Implementing without that module:
<lang ruby>separator = '/'
<syntaxhighlight lang="ruby">separator = '/'
path0, *paths = dirs.collect {|dir| dir.split(separator)}
path0, *paths = dirs.collect {|dir| dir.split(separator)}
uncommon_idx = path0.zip(*paths).index {|dirnames| dirnames.uniq.length > 1}
uncommon_idx = path0.zip(*paths).index {|dirnames| dirnames.uniq.length > 1}
uncommon_idx = path0.length unless uncommon_idx # if uncommon_idx==nil
uncommon_idx = path0.length unless uncommon_idx # if uncommon_idx==nil
common_directory = path0[0...uncommon_idx].join(separator) # => "/home/user1/tmp"</lang>
common_directory = path0[0...uncommon_idx].join(separator) # => "/home/user1/tmp"</syntaxhighlight>


or method version
or method version
<lang ruby>def common_directory_path(dirs, separator='/')
<syntaxhighlight lang="ruby">def common_directory_path(dirs, separator='/')
dir1, dir2 = dirs.minmax.map{|dir| dir.split(separator)}
dir1, dir2 = dirs.minmax.map{|dir| dir.split(separator)}
dir1.zip(dir2).take_while{|dn1,dn2| dn1==dn2}.map(&:first).join(separator)
dir1.zip(dir2).take_while{|dn1,dn2| dn1==dn2}.map(&:first).join(separator)
end
end


p common_directory_path(dirs) #=> "/home/user1/tmp"</lang>
p common_directory_path(dirs) #=> "/home/user1/tmp"</syntaxhighlight>


=={{header|Run BASIC}}==
=={{header|Run BASIC}}==
<lang runbasic>' ------------------------------------------
<syntaxhighlight lang="runbasic">' ------------------------------------------
' Find common directory to all directories
' Find common directory to all directories
' and directories common with other Paths
' and directories common with other Paths
Line 2,333: Line 2,750:
html "</TABLE>"
html "</TABLE>"
wait
wait
end</lang>
end</syntaxhighlight>
========= Common paths ================<br />
========= Common paths ================<br />
shows only the first few common paths..
shows only the first few common paths..
Line 2,374: Line 2,791:
Rust has specific types for owned and borrowed paths. PathBuf is an 'owned' pointer to a path, Path is a borrow; this is similar to String and str, respectively.
Rust has specific types for owned and borrowed paths. PathBuf is an 'owned' pointer to a path, Path is a borrow; this is similar to String and str, respectively.


<syntaxhighlight lang="rust">
<lang Rust>
use std::path::{Path, PathBuf};
use std::path::{Path, PathBuf};


Line 2,425: Line 2,842:
}
}
}
}
</syntaxhighlight>
</lang>


=={{header|Scala}}==
=={{header|Scala}}==
===Naive===
===Naive===
This simple solution solves the task as given, but has oddities for edge cases due to the implementation of java.lang.String#split.
This simple solution solves the task as given, but has oddities for edge cases due to the implementation of java.lang.String#split.
<lang Scala>object FindCommonDirectoryPath extends App {
<syntaxhighlight lang="scala">object FindCommonDirectoryPath extends App {
def commonPath(paths: List[String]): String = {
def commonPath(paths: List[String]): String = {
def common(a: List[String], b: List[String]): List[String] = (a, b) match {
def common(a: List[String], b: List[String]): List[String] = (a, b) match {
Line 2,446: Line 2,863:
)
)
println(commonPath(test))
println(commonPath(test))
}</lang>
}</syntaxhighlight>
Output:
Output:
<pre>/home/user1/tmp</pre>
<pre>/home/user1/tmp</pre>
===Advanced===
===Advanced===
This implementation will handle various edge cases and relative paths. It also includes any common trailing '/' but callers can remove this if desired.
This implementation will handle various edge cases and relative paths. It also includes any common trailing '/' but callers can remove this if desired.
<lang Scala>object FindCommonDirectoryPathRelative extends App {
<syntaxhighlight lang="scala">object FindCommonDirectoryPathRelative extends App {
def commonPath(paths: List[String]): String = {
def commonPath(paths: List[String]): String = {
val SEP = "/"
val SEP = "/"
Line 2,500: Line 2,917:
assert(commonPath(List("/a/a/", "/a/b/")) == "/a/")
assert(commonPath(List("/a/a/", "/a/b/")) == "/a/")
assert(commonPath(List("/a/b/", "/a/b/")) == "/a/b/")
assert(commonPath(List("/a/b/", "/a/b/")) == "/a/b/")
}</lang>
}</syntaxhighlight>


=={{header|Seed7}}==
=={{header|Seed7}}==
Line 2,510: Line 2,927:
but they need to make sure that a path does not end with a slash.
but they need to make sure that a path does not end with a slash.


<lang seed7>$ include "seed7_05.s7i";
<syntaxhighlight lang="seed7">$ include "seed7_05.s7i";


const func integer: commonLen (in array string: names, in char: sep) is func
const func integer: commonLen (in array string: names, in char: sep) is func
Line 2,551: Line 2,968:
writeln("Common path: " <& names[1][.. length]);
writeln("Common path: " <& names[1][.. length]);
end if;
end if;
end func;</lang>
end func;</syntaxhighlight>


Output:
Output:
Line 2,559: Line 2,976:


=={{header|Sidef}}==
=={{header|Sidef}}==
<lang ruby>var dirs = %w(
<syntaxhighlight lang="ruby">var dirs = %w(
/home/user1/tmp/coverage/test
/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/covert/operator
Line 2,567: Line 2,984:
var unique_pref = dirs.map{.split('/')}.abbrev.min_by{.len};
var unique_pref = dirs.map{.split('/')}.abbrev.min_by{.len};
var common_dir = [unique_pref, unique_pref.pop][0].join('/');
var common_dir = [unique_pref, unique_pref.pop][0].join('/');
say common_dir; # => /home/user1/tmp</lang>
say common_dir; # => /home/user1/tmp</syntaxhighlight>

=={{header|Standard ML}}==
<syntaxhighlight lang="sml">fun takeWhileEq ([], _) = []
| takeWhileEq (_, []) = []
| takeWhileEq (x :: xs, y :: ys) =
if x = y then x :: takeWhileEq (xs, ys) else []

fun commonPath sep =
let
val commonInit = fn [] => [] | x :: xs => foldl takeWhileEq x xs
and split = String.fields (fn c => c = sep)
and join = String.concatWith (str sep)
in
join o commonInit o map split
end

val paths = [
"/home/user1/tmp/coverage/test",
"/home/user1/tmp/covert/operator",
"/home/user1/tmp/coven/members"
]

val () = print (commonPath #"/" paths ^ "\n")</syntaxhighlight>


=={{header|Swift}}==
=={{header|Swift}}==
Line 2,573: Line 3,013:
The below solution works only in swift in Linux.
The below solution works only in swift in Linux.


<lang swift>import Foundation
<syntaxhighlight lang="swift">import Foundation




Line 2,589: Line 3,029:


var output:String = getPrefix(test)!
var output:String = getPrefix(test)!
print(output)</lang>
print(output)</syntaxhighlight>

===Works on MacOS===
<syntaxhighlight lang="swift">
import Foundation

func commonPrefix<T: Equatable>(_ lhs: [T], _ rhs: [T]) -> [T] {
for tryLen in (0...min(lhs.count,rhs.count)).reversed() {
if lhs.starts(with: rhs.prefix(tryLen)) {
return Array<T>(rhs.prefix(tryLen))
}
}
return []
}

var test = ["/home/user1/tmp/coverage/test",
"/home/user1/tmp/covert/operator",
"/home/user1/tmp/coven/members"]

let lcp: String = test.reduce("") { lhs, rhs in
if !lhs.isEmpty {
var commonSoFar = commonPrefix(
lhs.components(separatedBy: "/"),
rhs.components(separatedBy: "/")
)
return commonSoFar.joined(separator: "/")
}
return rhs
}
print("Longest common path: \(lcp)")

// Longest common path: /home/user1/tmp
</syntaxhighlight>


=={{header|Tcl}}==
=={{header|Tcl}}==
<lang tcl>package require Tcl 8.5
<syntaxhighlight lang="tcl">package require Tcl 8.5
proc pop {varname} {
proc pop {varname} {
upvar 1 $varname var
upvar 1 $varname var
Line 2,610: Line 3,082:
}
}
return [join $parts $separator]
return [join $parts $separator]
}</lang>
}</syntaxhighlight>


<pre>% common_prefix {/home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members}
<pre>% common_prefix {/home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members}
Line 2,616: Line 3,088:


=={{header|TUSCRIPT}}==
=={{header|TUSCRIPT}}==
<lang tuscript>
<syntaxhighlight lang="tuscript">
$$ MODE TUSCRIPT
$$ MODE TUSCRIPT
common=""
common=""
Line 2,631: Line 3,103:
ENDIF
ENDIF
ENDLOOP
ENDLOOP
</syntaxhighlight>
</lang>
Output:
Output:
<pre>
<pre>
Line 2,639: Line 3,111:
=={{header|UNIX Shell}}==
=={{header|UNIX Shell}}==
The following is a pure Bourne Shell solution. The while loop controls the maximum depth to check paths.
The following is a pure Bourne Shell solution. The while loop controls the maximum depth to check paths.
<lang bash>
<syntaxhighlight lang="bash">
#!/bin/sh
#!/bin/sh


Line 2,660: Line 3,132:
i=`expr $i + 1`
i=`expr $i + 1`
done
done
</syntaxhighlight>
</lang>


=={{header|Ursala}}==
=={{header|Ursala}}==
The algorithm is to lex the paths into component directory names, and then find the greatest common prefix of those.
The algorithm is to lex the paths into component directory names, and then find the greatest common prefix of those.
<lang Ursala>#import std
<syntaxhighlight lang="ursala">#import std


comdir"s" "p" = mat"s" reduce(gcp,0) (map sep "s") "p"</lang>
comdir"s" "p" = mat"s" reduce(gcp,0) (map sep "s") "p"</syntaxhighlight>
where <code>"s"</code> is a dummy variable representing the separator, <code>"p"</code> is a dummy variable representing the list of paths, and
where <code>"s"</code> is a dummy variable representing the separator, <code>"p"</code> is a dummy variable representing the list of paths, and
*<code>sep</code> is second order function in the standard library that takes a separator character and returns a lexer mapping a string containing the separator to a list of the substrings found between occurrences of it
*<code>sep</code> is second order function in the standard library that takes a separator character and returns a lexer mapping a string containing the separator to a list of the substrings found between occurrences of it
Line 2,674: Line 3,146:
*<code>mat</code> is a second order function in the standard library that takes a separator character and returns a function that flattens a list of strings into a single string with copies of the separator inserted between them
*<code>mat</code> is a second order function in the standard library that takes a separator character and returns a function that flattens a list of strings into a single string with copies of the separator inserted between them
Here is a version using operators instead of mnemonics for <code>map</code> and <code>reduce</code>.
Here is a version using operators instead of mnemonics for <code>map</code> and <code>reduce</code>.
<lang Ursala>comdir"s" "p" = mat"s" gcp:-0 sep"s"* "p"</lang>
<syntaxhighlight lang="ursala">comdir"s" "p" = mat"s" gcp:-0 sep"s"* "p"</syntaxhighlight>
Here is one in partly point-free form, using the composition operator (<code>+</code>).
Here is one in partly point-free form, using the composition operator (<code>+</code>).
<lang Ursala>comdir"s" = mat"s"+ gcp:-0+ sep"s"*</lang>
<syntaxhighlight lang="ursala">comdir"s" = mat"s"+ gcp:-0+ sep"s"*</syntaxhighlight>
Here it is in point-free form.
Here it is in point-free form.
<lang Ursala>comdir = +^/mat gcp:-0++ *+ sep</lang>
<syntaxhighlight lang="ursala">comdir = +^/mat gcp:-0++ *+ sep</syntaxhighlight>
test program:
test program:
<lang Ursala>#cast %s
<syntaxhighlight lang="ursala">#cast %s


test =
test =
Line 2,687: Line 3,159:
'/home/user1/tmp/coverage/test',
'/home/user1/tmp/coverage/test',
'/home/user1/tmp/covert/operator',
'/home/user1/tmp/covert/operator',
'/home/user1/tmp/coven/members'></lang>
'/home/user1/tmp/coven/members'></syntaxhighlight>
output:
output:
<pre>'/home/user1/tmp'</pre>
<pre>'/home/user1/tmp'</pre>
Line 2,695: Line 3,167:
=={{header|VBScript}}==
=={{header|VBScript}}==
{{works with|Windows Script Host|*}}
{{works with|Windows Script Host|*}}
<syntaxhighlight lang="vbscript">
<lang VBScript>
' Read the list of paths (newline-separated) into an array...
' Read the list of paths (newline-separated) into an array...
strPaths = Split(WScript.StdIn.ReadAll, vbCrLf)
strPaths = Split(WScript.StdIn.ReadAll, vbCrLf)
Line 2,727: Line 3,199:
' Remove the final "/"...
' Remove the final "/"...
WScript.Echo Left(strPath, Len(strPath) - 1)
WScript.Echo Left(strPath, Len(strPath) - 1)
</syntaxhighlight>
</lang>


=={{header|Visual Basic}}==
=={{header|Visual Basic}}==
Line 2,734: Line 3,206:
{{works with|VBA|6.5}}
{{works with|VBA|6.5}}
{{works with|VBA|7.1}}
{{works with|VBA|7.1}}
<lang vb>Public Function CommonDirectoryPath(ParamArray Paths()) As String
<syntaxhighlight lang="vb">Public Function CommonDirectoryPath(ParamArray Paths()) As String
Dim v As Variant
Dim v As Variant
Dim Path() As String, s As String
Dim Path() As String, s As String
Line 2,793: Line 3,265:
"/"
"/"


End Sub</lang>
End Sub</syntaxhighlight>


=={{header|Wren}}==
=={{header|Wren}}==
<lang ecmascript>var findCommonDir = Fn.new { |paths, sep|
<syntaxhighlight lang="wren">var findCommonDir = Fn.new { |paths, sep|
var count = paths.count
var count = paths.count
if (count == 0) return ""
if (count == 0) return ""
Line 2,825: Line 3,297:
]
]
System.write("The common directory path is: ")
System.write("The common directory path is: ")
System.print(findCommonDir.call(paths, "/"))</lang>
System.print(findCommonDir.call(paths, "/"))</syntaxhighlight>


{{out}}
{{out}}
Line 2,831: Line 3,303:
The common directory path is: /home/user1/tmp
The common directory path is: /home/user1/tmp
</pre>
</pre>

=={{header|Yabasic}}==
{{trans|GW-BASIC}}
<syntaxhighlight lang="yabasic">x$ = "/home/user1/tmp/coverage/test"
y$ = "/home/user1/tmp/covert/operator"
z$ = "/home/user1/tmp/coven/members"

a = len(x$)
if a > len(y$) a = len(y$)
if a > len(z$) a = len(z$)
for i = 1 to a
if mid$(x$, i, 1) <> mid$(y$, i, 1) break
next i
a = i - 1

for i = 1 to a
if mid$(x$, i, 1) <> mid$(z$, i, 1) break
next i
a = i - 1

if mid$(x$, i, 1) <> "/" then
for i = a to 1 step -1
if "/" = mid$(x$, i, 1) break
next i
fi

REM Task description says no trailing slash, so...
a = i - 1
print "Common path is '", left$(x$, a), "'"</syntaxhighlight>


=={{header|zkl}}==
=={{header|zkl}}==
<lang zkl>dirs:=T("/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator",
<syntaxhighlight lang="zkl">dirs:=T("/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator",
"/home/user1/tmp/coven/members");
"/home/user1/tmp/coven/members");
n:=Utils.zipWith('==,dirs.xplode()).find(False); // character pos which differs
n:=Utils.zipWith('==,dirs.xplode()).find(False); // character pos which differs
n=dirs[0][0,n].rfind("/"); // find last "/"
n=dirs[0][0,n].rfind("/"); // find last "/"
dirs[0][0,n];</lang>
dirs[0][0,n];</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>

Revision as of 20:23, 3 April 2024

Task
Find common directory path
You are encouraged to solve this task according to the task description, using any language you may know.

Create a routine that, given a set of strings representing directory paths and a single character directory separator, will return a string representing that part of the directory tree that is common to all the directories.

Test your routine using the forward slash '/' character as the directory separator and the following three strings as input paths:

     '/home/user1/tmp/coverage/test'
     '/home/user1/tmp/covert/operator'
     '/home/user1/tmp/coven/members'

Note: The resultant path should be the valid directory '/home/user1/tmp' and not the longest common string '/home/user1/tmp/cove'.

If your language has a routine that performs this function (even if it does not have a changeable separator character), then mention it as part of the task.

Other tasks related to string operations:
Metrics
Counting
Remove/replace
Anagrams/Derangements/shuffling
Find/Search/Determine
Formatting
Song lyrics/poems/Mad Libs/phrases
Tokenize
Sequences



11l

Translation of: C
F find_common_directory_path(paths, sep = ‘/’)
   V pos = 0
   L
      L(path) paths
         I pos < path.len & path[pos] == paths[0][pos]
            L.continue

         L pos > 0
            pos--
            I paths[0][pos] == sep
               L.break
         R paths[0][0.<pos]
      pos++

print(find_common_directory_path([
   ‘/home/user1/tmp/coverage/test’,
   ‘/home/user1/tmp/covert/operator’,
   ‘/home/user1/tmp/coven/members’]))
Output:
/home/user1/tmp

Ada

with Ada.Text_IO;  use Ada.Text_IO;

procedure Test_Common_Path is
   function "rem" (A, B : String) return String is
      Slash : Integer := A'First; -- At the last slash seen in A
      At_A  : Integer := A'first;
      At_B  : Integer := B'first;
   begin
      loop
         if At_A > A'Last then
            if At_B > B'Last or else B (At_B) = '/' then
               return A;
            else
               return A (A'First..Slash - 1);
            end if;
         elsif At_B > B'Last then
            if A (At_A) = '/' then -- A cannot be shorter than B here
               return B;
            else
               return A (A'First..Slash - 1);
            end if;
         elsif A (At_A) /= B (At_B) then
            return A (A'First..Slash - 1);
         elsif A (At_A) = '/' then
            Slash := At_A;
         end if;
         At_A := At_A + 1;
         At_B := At_B + 1;
      end loop;      
   end "rem";
begin
   Put_Line
   (  "/home/user1/tmp/coverage/test" rem
      "/home/user1/tmp/covert/operator" rem
      "/home/user1/tmp/coven/members"
   );
end Test_Common_Path;

Output:

/home/user1/tmp

Aime

cdp(...)
{
    integer e;
    record r;
    text s;

    ucall(r_add, 1, r, 0);

    if (~r) {
        s = r.low;
        s = s.cut(0, e = b_trace(s, prefix(s, r.high), '/'));
        s = ~s || e == -1 ? s : "/";
    }

    s;
}

main(void)
{
    o_(cdp("/home/user1/tmp/coverage/test",
           "/home/user1/tmp/covert/operator",
           "/home/user1/tmp/coven/members"), "\n");

    0;
}

ALGOL 68

Works with: ALGOL 68 version Revision 1 - no extensions to language used
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny
Works with: ELLA ALGOL 68 version Any (with appropriate job cards) - tested with release 1.8-8d
# Utilities code #

CHAR dir sep = "/"; # Assume POSIX #

PROC dir name = (STRING dir)STRING: (
  STRING out;
  FOR pos FROM UPB dir BY -1 TO LWB dir DO
    IF dir[pos] = dir sep THEN
      out := dir[:pos-1];
      GO TO out exit
    FI
  OD;
  # else: # out:=""; 
  out exit: out
);

PROC shortest = ([]STRING string list)STRING: (
  INT min := max int;
  INT min key := LWB string list - 1;
  FOR key FROM LWB string list TO UPB string list DO
    IF UPB string list[key][@1] < min THEN
      min := UPB string list[key][@1];
      min key := key
    FI
  OD;
  string list[min key]
);

# Actual code #

PROC common prefix = ([]STRING strings)STRING: (
  IF LWB strings EQ UPB strings THEN
    # exit: # strings[LWB strings]
  ELSE
    STRING prefix := shortest(strings);
    FOR pos FROM LWB prefix TO UPB prefix DO
      CHAR first = prefix[pos];
      FOR key FROM LWB strings+1 TO UPB strings DO
        IF strings[key][@1][pos] NE first THEN 
          prefix := prefix[:pos-1];
          GO TO prefix exit
        FI
      OD
    OD;
    prefix exit: prefix 
  FI
);

# Test code #

test:(
  []STRING dir list = (
    "/home/user1/tmp/coverage/test",
    "/home/user1/tmp/covert/operator",
    "/home/user1/tmp/coven/members"
  );
  print((dir name(common prefix(dir list)), new line))
)

Output:

/home/user1/tmp

AppleScript

on commonDirectoryPath(thePaths, separator)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to separator
    set path1 to thePaths's beginning
    set path1Components to path1's text items
    set maxC to (count path1Components)
    repeat with nextPath in (thePaths's rest)
        if (maxC = 0) then exit repeat
        set theseComponents to nextPath's text items
        set componentCount to (count theseComponents)
        if (componentCount < maxC) then set maxC to componentCount
        repeat with c from 1 to maxC
            if (theseComponents's item c  path1Components's item c) then
                set maxC to c - 1
                exit repeat
            end if
        end repeat
    end repeat
    if (maxC > 0) then
        set commonPath to path1's text 1 thru text item maxC
    else
        set commonPath to ""
    end if
    set AppleScript's text item delimiters to astid
    
    return commonPath
end commonDirectoryPath

return commonDirectoryPath({"/home/user1/tmp/coverage/test", ¬
    "/home/user1/tmp/covert/operator", ¬
    "/home/user1/tmp/coven/members"}, "/")
Output:
"/home/user1/tmp"

Arturo

commonPathPrefix: function [lst][
    paths: map lst => [split.by:"/" &]

    common: new []
    firstPath: first paths
    loop .with:'i firstPath 'part [
        found: true
        loop paths 'p [
            if part <> get p i [
                found: false
                break
            ]
        ]
        if found -> 'common ++ part
    ]
    return join.with:"/" common
]

print commonPathPrefix [
    "/home/user1/tmp/coverage/test"
    "/home/user1/tmp/covert/operator"
    "/home/user1/tmp/coven/members"
]
Output:
/home/user1/tmp

AutoHotkey

Dir1 := "/home/user1/tmp/coverage/test"
Dir2 := "/home/user1/tmp/covert/operator"
Dir3 := "/home/user1/tmp/coven/members"

StringSplit, Dir1_, Dir1, /
StringSplit, Dir2_, Dir2, /
StringSplit, Dir3_, Dir3, /

Loop
    If  (Dir1_%A_Index% = Dir2_%A_Index%)
    And (Dir1_%A_Index% = Dir3_%A_Index%)
        Result .= (A_Index=1 ? "" : "/") Dir1_%A_Index%
    Else Break

MsgBox, % Result

Message box shows:

/home/user1/tmp

AWK

# Finds the longest common directory of paths[1], paths[2], ...,
# paths[count], where sep is a single-character directory separator.
function common_dir(paths, count, sep,    b, c, f, i, j, p) {
	if (count < 1)
		return ""

	p = ""	# Longest common prefix
	f = 0	# Final index before last sep

	# Loop for c = each character of paths[1].
	for (i = 1; i <= length(paths[1]); i++) {
		c = substr(paths[1], i, 1)

		# If c is not the same in paths[2], ..., paths[count]
		# then break both loops.
		b = 0
		for (j = 2; j <= count; j++) {
			if (c != substr(paths[j], i, 1)) {
				b = 1
				break
			}
		}
		if (b)
			break

		# Append c to prefix. Update f.
		p = p c
		if (c == sep)
			f = i - 1
	}

	# Return only f characters of prefix.
	return substr(p, 1, f)
}

BEGIN {
	a[1] = "/home/user1/tmp/coverage/test"
	a[2] = "/home/user1/tmp/covert/operator"
	a[3] = "/home/user1/tmp/coven/members"
	print common_dir(a, 3, "/")
}

Prints /home/user1/tmp.

BASIC

Works with: QuickBASIC version 7
Works with: FreeBASIC

This version is a little smarter than the one above... but not much. This version could be turned into an actual useful utility by changing it to compare command-line parameters, instead of built-in data.

Also, under FreeBASIC, the pathSep arg to commonPath$ could be made optional, or even system-dependent.

DECLARE FUNCTION commonPath$ (paths() AS STRING, pathSep AS STRING)

DATA "/home/user2", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"

DIM x(0 TO 2) AS STRING, n AS INTEGER
FOR n = 0 TO 2
    READ x(n)
NEXT

PRINT "Common path is '"; commonPath$(x(), "/"); "'"

FUNCTION commonPath$ (paths() AS STRING, pathSep AS STRING)
    DIM tmpint1 AS INTEGER, tmpint2 AS INTEGER, tmpstr1 AS STRING, tmpstr2 AS STRING
    DIM L0 AS INTEGER, L1 AS INTEGER, lowerbound AS INTEGER, upperbound AS INTEGER
    lowerbound = LBOUND(paths): upperbound = UBOUND(paths)

    IF (lowerbound) = upperbound THEN       'Some quick error checking...
        commonPath$ = paths(lowerbound)
    ELSEIF lowerbound > upperbound THEN     'How in the...?
        commonPath$ = ""
    ELSE
        tmpstr1 = paths(lowerbound)

        FOR L0 = (lowerbound + 1) TO upperbound 'Find common strings.
            tmpstr2 = paths(L0)
            tmpint1 = LEN(tmpstr1)
            tmpint2 = LEN(tmpstr2)
            IF tmpint1 > tmpint2 THEN tmpint1 = tmpint2
            FOR L1 = 1 TO tmpint1
                IF MID$(tmpstr1, L1, 1) <> MID$(tmpstr2, L1, 1) THEN
                    tmpint1 = L1 - 1
                    EXIT FOR
                END IF
            NEXT
            tmpstr1 = LEFT$(tmpstr1, tmpint1)
        NEXT

        IF RIGHT$(tmpstr1, 1) <> pathSep THEN
            FOR L1 = tmpint1 TO 2 STEP -1
                IF (pathSep) = MID$(tmpstr1, L1, 1) THEN
                    tmpstr1 = LEFT$(tmpstr1, L1 - 1)
                    EXIT FOR
                END IF
            NEXT
            IF LEN(tmpstr1) = tmpint1 THEN tmpstr1 = ""
        ELSEIF tmpint1 > 1 THEN
            tmpstr1 = LEFT$(tmpstr1, tmpint1 - 1)
        END IF

        commonPath$ = tmpstr1
    END IF
END FUNCTION

BASIC256

Translation of: GW-BASIC
x = "/home/user1/tmp/coverage/test"
y = "/home/user1/tmp/covert/operator"
z = "/home/user1/tmp/coven/members"

a = length(x)
if a > length(y) then a = length(y)
if a > length(z) then a = length(z)
for i = 1 to a
    if mid(x, i, 1) <> mid(y, i, 1) then exit for
next i
a = i - 1

for i = 1 to a
    if mid(x, i, 1) <> mid(z, i, 1) then exit for
next i
a = i - 1

if mid(x, i, 1) <> "/" then
    for i = a to 1 step -1
        if "/" = mid(x, i, 1) then exit for
    next i
end if

REM Task description says no trailing slash, so...
a = i - 1
print "Common path is '"; left(x, a); "'"

Batch File

@echo off
setlocal enabledelayedexpansion

call:commonpath /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members
pause>nul
exit /b

:commonpath
setlocal enabledelayedexpansion

for %%i in (%*) do (
  set /a args+=1
  set arg!args!=%%i
  set fullarg!args!=%%i
)
for /l %%i in (1,1,%args%) do set fullarg%%i=!fullarg%%i:/= !

for /l %%i in (1,1,%args%) do (
  set tempcount=0
  for %%j in (!fullarg%%i!) do (
    set /a tempcount+=1
    set arg%%it!tempcount!=%%j
    set arg%%itokencount=!tempcount!
  )
)

set mintokencount=%arg1tokencount%
set leasttokens=1
for /l %%i in (1,1,%args%) do (
  set currenttokencount=!arg%%itokencount!
  if !currenttokencount! lss !mintokencount! (
    set mintokencount=!currenttokencount!
    set leasttokens=%%i
  )
)

for /l %%i in (1,1,%mintokencount%) do set commonpath%%i=!arg%leasttokens%t%%i!

for /l %%i in (1,1,%mintokencount%) do (
  for /l %%j in (1,1,%args%) do (
    set currentpath=!arg%%jt%%i!
    if !currentpath!==!commonpath%%i! set pathtokens%%j=%%i
  )
)

set minpathtokens=%pathtokens1%
set leastpathtokens=1
for /l %%i in (1,1,%args%) do (
  set currentpathtokencount=!pathtokens%%i!
  if !currentpathtokencount! lss !minpathtokens! (
    set minpathtokencount=!currentpathtokencount!
    set leastpathtokens=%%i
  )
)

set commonpath=/
for /l %%i in (1,1,!pathtokens%leastpathtokens%!) do set commonpath=!commonpath!!arg%leastpathtokens%t%%i!/
echo %commonpath%

endlocal
exit /b
Output:
/home/user1/tmp/

BBC BASIC

      DIM path$(3)
      
      path$(1) = "/home/user1/tmp/coverage/test"
      path$(2) = "/home/user1/tmp/covert/operator"
      path$(3) = "/home/user1/tmp/coven/members"
      
      PRINT FNcommonpath(path$(), "/")
      END
      
      DEF FNcommonpath(p$(), s$)
      LOCAL I%, J%, O%
      REPEAT
        O% = I%
        I% = INSTR(p$(1), s$, I%+1)
        FOR J% = 2 TO DIM(p$(), 1)
          IF LEFT$(p$(1), I%) <> LEFT$(p$(J%), I%) EXIT REPEAT
        NEXT J%
      UNTIL I% = 0
      = LEFT$(p$(1), O%-1)

C

#include <stdio.h>

int common_len(const char *const *names, int n, char sep)
{
	int i, pos;
	for (pos = 0; ; pos++) {
		for (i = 0; i < n; i++) {
			if (names[i][pos] != '\0' &&
					names[i][pos] == names[0][pos])
				continue;

			/* backtrack */
			while (pos > 0 && names[0][--pos] != sep);
			return pos;
		}
	}

	return 0;
}

int main()
{
	const char *names[] = {
		"/home/user1/tmp/coverage/test",
		"/home/user1/tmp/covert/operator",
		"/home/user1/tmp/coven/members",
	};
	int len = common_len(names, sizeof(names) / sizeof(const char*), '/');

	if (!len) printf("No common path\n");
	else      printf("Common path: %.*s\n", len, names[0]);

	return 0;
}

output:

Common path: /home/user1/tmp

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RosettaCodeTasks
{

	class Program
	{
		static void Main ( string[ ] args )
		{
			FindCommonDirectoryPath.Test ( );
		}

	}

	class FindCommonDirectoryPath
	{
		public static void Test ( )
		{
			Console.WriteLine ( "Find Common Directory Path" );
			Console.WriteLine ( );
			List<string> PathSet1 = new List<string> ( );
			PathSet1.Add ( "/home/user1/tmp/coverage/test" );
			PathSet1.Add ( "/home/user1/tmp/covert/operator" );
			PathSet1.Add ( "/home/user1/tmp/coven/members" );
			Console.WriteLine("Path Set 1 (All Absolute Paths):");
			foreach ( string path in PathSet1 )
			{
				Console.WriteLine ( path );
			}
			Console.WriteLine ( "Path Set 1 Common Path: {0}", FindCommonPath ( "/", PathSet1 ) );
		}
		public static string FindCommonPath ( string Separator, List<string> Paths )
		{
			string CommonPath = String.Empty;
			List<string> SeparatedPath = Paths
				.First ( str => str.Length == Paths.Max ( st2 => st2.Length ) )
				.Split ( new string[ ] { Separator }, StringSplitOptions.RemoveEmptyEntries )
				.ToList ( );

			foreach ( string PathSegment in SeparatedPath.AsEnumerable ( ) )
			{
				if ( CommonPath.Length == 0 && Paths.All ( str => str.StartsWith ( PathSegment ) ) )
				{
					CommonPath = PathSegment;
				}
				else if ( Paths.All ( str => str.StartsWith ( CommonPath + Separator + PathSegment ) ) )
				{
					CommonPath += Separator + PathSegment;
				}
				else
				{
					break;
				}
			}
			
			return CommonPath;
		}
	}
}

C++

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

std::string longestPath( const std::vector<std::string> & , char ) ;

int main( ) {
   std::string dirs[ ] = {
      "/home/user1/tmp/coverage/test" ,
      "/home/user1/tmp/covert/operator" ,
      "/home/user1/tmp/coven/members" } ;
   std::vector<std::string> myDirs ( dirs , dirs + 3 ) ;
   std::cout << "The longest common path of the given directories is "
             << longestPath( myDirs , '/' ) << "!\n" ;
   return 0 ;
}

std::string longestPath( const std::vector<std::string> & dirs , char separator ) {
   std::vector<std::string>::const_iterator vsi = dirs.begin( ) ;
   int maxCharactersCommon = vsi->length( ) ;
   std::string compareString = *vsi ;
   for ( vsi = dirs.begin( ) + 1 ; vsi != dirs.end( ) ; vsi++ ) {
      std::pair<std::string::const_iterator , std::string::const_iterator> p = 
	 std::mismatch( compareString.begin( ) , compareString.end( ) , vsi->begin( ) ) ;
      if (( p.first - compareString.begin( ) ) < maxCharactersCommon ) 
	 maxCharactersCommon = p.first - compareString.begin( ) ;
   }
   std::string::size_type found = compareString.rfind( separator , maxCharactersCommon ) ;
   return compareString.substr( 0 , found ) ;
}

Output:

The longest common path of the given directories is /home/user1/tmp!

Clojure

(use '[clojure.string :only [join,split]]) 

(defn common-prefix [sep paths]
  (let [parts-per-path (map #(split % (re-pattern sep)) paths)
        parts-per-position (apply map vector parts-per-path)]
    (join sep
      (for [parts parts-per-position :while (apply = parts)]
        (first parts)))))

(println
  (common-prefix "/"
    ["/home/user1/tmp/coverage/test"
     "/home/user1/tmp/covert/operator"
     "/home/user1/tmp/coven/members"]))


Common Lisp

The strings represent file paths, so instead of treating them as simple strings, this uses the specialized pathname functions, which are more robust and generic.

(defun common-directory-path (&rest paths)
  (do* ((pathnames (mapcar #'(lambda (path) (cdr (pathname-directory (pathname path)))) paths)) ; convert strings to lists of subdirectories
     (rem pathnames (cdr rem))
     (pos (length (first rem))) ) ; position of first mismatched element
    ((null (cdr rem)) (make-pathname :directory (cons :absolute (subseq (first pathnames) 0 pos)))) ; take the common sublists and convert back to a pathname
  (setq pos (min pos (mismatch (first rem) (second rem) :test #'string-equal))) )) ; compare two paths


Output:
(common-directory-path "/home/user1/tmp/coverage/test" "/home/user1/tmp/covert/operator" "/home/user1/tmp/coven/members")
 ==> #P"/home/user1/tmp/"

D

This code uses the std.algorithm.commonPrefix function that finds the common prefix of two ranges.

import std.stdio, std.string, std.algorithm, std.path, std.array;

string commonDirPath(in string[] paths, in string sep = "/") pure {
    if (paths.empty)
        return null;
    return paths.map!(p => p.split(sep)).reduce!commonPrefix.join(sep);
}

void main() {
    immutable paths = ["/home/user1/tmp/coverage/test",
                       "/home/user1/tmp/covert/operator",
                       "/home/user1/tmp/coven/members"];
    writeln(`The common path is: "`, paths.commonDirPath, '"');
}
Output:
The common path is: "/home/user1/tmp"

Delphi

program Find_common_directory_path;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

function FindCommonPath(Separator: Char; Paths: TArray<string>): string;
var
  SeparatedPath: array of TArray<string>;
  minLength, index: Integer;
  isSame: Boolean;
  j, i: Integer;
  cmp: string;
begin
  SetLength(SeparatedPath, length(Paths));
  minLength := MaxInt;
  for i := 0 to High(SeparatedPath) do
  begin
    SeparatedPath[i] := Paths[i].Split([Separator]);
    if minLength > length(SeparatedPath[i]) then
      minLength := length(SeparatedPath[i]);
  end;

  index := -1;

  for i := 0 to minLength - 1 do
  begin
    isSame := True;
    cmp := SeparatedPath[0][i];
    for j := 1 to High(SeparatedPath) do
      if SeparatedPath[j][i] <> cmp then
      begin
        isSame := False;
        Break;
      end;
    if not isSame then
    begin
      index := i - 1;
      Break;
    end;
  end;

  Result := '';
  if index >= 0 then
    for i := 0 to index do
    begin
      Result := Result + SeparatedPath[0][i];
      if i < index then
        Result := Result + Separator;
    end;
end;

begin
  Writeln(FindCommonPath('/', [
    '/home/user1/tmp/coverage/test',
    '/home/user1/tmp/covert/operator',
    '/home/user1/tmp/coven/members']));
  Readln;
end.
Output:
/home/user1/tmp

EasyLang

Translation of: AWK
func$ comdir path$[] .
   for i = 1 to len path$[1]
      c$ = substr path$[1] i 1
      for j = 2 to len path$[]
         if c$ <> substr path$[j] i 1
            break 2
         .
      .
      if c$ = "/"
         f = i - 1
      .
   .
   return substr path$[1] 1 f
.
a$[] &= "/home/user1/tmp/coverage/test"
a$[] &= "/home/user1/tmp/covert/operator"
a$[] &= "/home/user1/tmp/coven/members"
print comdir a$[]

Elixir

Translation of: Ruby
defmodule RC do
  def common_directory_path(dirs, separator \\ "/") do
    dir1 = Enum.min(dirs) |> String.split(separator)
    dir2 = Enum.max(dirs) |> String.split(separator)
    Enum.zip(dir1,dir2) |> Enum.take_while(fn {a,b} -> a==b end)
                        |> Enum.map_join(separator, fn {a,a} -> a end)
  end
end

dirs = ~w( /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members )
IO.inspect RC.common_directory_path(dirs)
Output:
"/home/user1/tmp"

Erlang

-module( find_common_directory ).

-export( [path/2, task/0] ).

path( [Path | T], _Separator ) -> filename:join( lists:foldl(fun keep_common/2, filename:split(Path), [filename:split(X) || X <- T]) ).

task() -> path( ["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"], "/" ).



keep_common( Components, Acc ) -> [X || X <- Components, Y <- Acc, X =:= Y].
Output:
78> find_common_directory:task().
"/home/user1/tmp"

F#

open System

let (|SeqNode|SeqEmpty|) s =
    if Seq.isEmpty s then SeqEmpty
    else SeqNode ((Seq.head s), Seq.skip 1 s)

[<EntryPoint>]
let main args =
    let splitBySeparator (str : string) = Seq.ofArray (str.Split('/'))

    let rec common2 acc = function
        | SeqEmpty -> Seq.ofList (List.rev acc)
        | SeqNode((p1, p2), rest) ->
            if p1 = p2 then common2 (p1::acc) rest
            else Seq.ofList (List.rev acc)

    let commonPrefix paths =
        match Array.length(paths) with
        | 0 -> [||]
        | 1 -> Seq.toArray (splitBySeparator paths.[0])
        | _ ->
            let argseq = Seq.ofArray paths
            Seq.fold (
                fun (acc : seq<string>) items ->
                    common2 [] (List.ofSeq (Seq.zip acc (splitBySeparator items)))
            ) (splitBySeparator (Seq.head argseq)) (Seq.skip 1 argseq)
            |> Seq.toArray

    printfn "The common preffix is: %A" (String.Join("/", (commonPrefix args)))
    0

Output for the given task input

The common preffix is: "/home/user1/tmp"

Factor

: take-shorter ( seq1 seq2 -- shorter )
    [ shorter? ] 2keep ? ;

: common-head ( seq1 seq2 -- head )
    2dup mismatch [ nip head ] [ take-shorter ] if* ;

: common-prefix-1 ( file1 file2 separator -- prefix )
    [ common-head ] dip '[ _ = not ] trim-tail ;

: common-prefix ( seq separator -- prefix )
    [ ] swap '[ _ common-prefix-1 ] map-reduce ;
( scratchpad ) {
                   "/home/user1/tmp/coverage/test"
                   "/home/user1/tmp/covert/operator"
                   "/home/user1/tmp/coven/members" 
               } CHAR: / common-prefix .
"/home/user1/tmp/"

FreeBASIC

Translation of: Visual Basic
' compile: fbc.exe -s console cdp.bas

Function CommonDirectoryPath Cdecl(count As Integer, ...) As String
Dim As String Path(), s
Dim As Integer i, j, k = 1
Dim arg As Any Ptr
Const PATH_SEPARATOR As String = "/"
 
  arg = va_first()
  ReDim Preserve Path(1 To count)
  For i = 1 To count
    Path(i) = *Va_Arg(arg, ZString Ptr)
	Print Path(i)
    arg = va_next(arg, ZString Ptr)
  Next i
  
  Do
    For i = 1 To count
      If i > 1 Then
        If InStr(k, Path(i), PATH_SEPARATOR) <> j Then
          Exit Do
        ElseIf Left(Path(i), j) <> Left(Path(1), j) Then
          Exit Do
        End If
      Else
        j = InStr(k, Path(i), PATH_SEPARATOR)
        If j = 0 Then
          Exit Do
        End If
      End If
    Next i
    s = Left(Path(1), j + CLng(k <> 1))
    k = j + 1
  Loop
  Return s
  
End Function


' testing the above function

Print CommonDirectoryPath( 3, _
  "/home/user1/tmp/coverage/test", _
  "/home/user1/tmp/covert/operator", _
  "/home/user1/tmp/coven/members") & " <- common"
Print  
 
Print CommonDirectoryPath( 4, _
  "/home/user1/tmp/coverage/test", _
  "/home/user1/tmp/covert/operator", _
  "/home/user1/tmp/coven/members", _
  "/home/user1/abc/coven/members") & " <- common"
Print    

Print CommonDirectoryPath( 3, _
  "/home/user1/tmp/coverage/test", _
  "/hope/user1/tmp/covert/operator", _
  "/home/user1/tmp/coven/members") & " <- common"
Print    

' empty keyboard buffer
While Inkey <> "" : Wend
Print : Print "hit any key to end program"
Sleep
End
Output:
/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members
/home/user1/tmp <- common

/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members
/home/user1/abc/coven/members
/home/user1 <- common

/home/user1/tmp/coverage/test
/hope/user1/tmp/covert/operator
/home/user1/tmp/coven/members
/ <- common

FutureBasic

include "NSLog.incl"

local fn FindCommonDirectoryPath
  CFArrayRef path1 = fn StringComponentsSeparatedByString( @"/home/user1/tmp/coverage/test",   @"/" )
  CFArrayRef path2 = fn StringComponentsSeparatedByString( @"/home/user1/tmp/covert/operator", @"/" )
  CFArrayRef path3 = fn StringComponentsSeparatedByString( @"/home/user1/tmp/coven/members",   @"/" )
  long    i, count = fn ArrayCount( path1 )
  CFMutableStringRef mutStr = fn MutableStringWithCapacity( 0 )
  
  for i = 0 to count - 1
    if ( fn StringIsEqual( path1[i], path2[i] ) ) and ( fn StringIsEqual( path2[i], path3[i] ) )
      MutableStringAppendString( mutStr, fn StringWithFormat( @"%@/\b", path1[i] ) )
    else
      exit for
    end if
  next
  NSLog( @"%@", mutstr )
end fn

fn FindCommonDirectoryPath

HandleEvents
Output:
/home/user1/tmp/

Gambas

Public Sub Main()
Dim sFolder As String[] = ["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"]
Dim sSame As String
Dim siCount As Short = 1

Do
  If Mid(sFolder[0], siCount, 1) = Mid(sFolder[1], siCount, 1) And Mid(sFolder[0], siCount, 1) = Mid(sFolder[2], siCount, 1) Then
    sSame &= Mid(sFolder[0], siCount, 1)
  Else
    Break
  End If
  Inc siCount
Loop

Print Mid(sSame, 1, RInStr(sSame, "/") - 1)

End

Output:

/home/user1/tmp

Go

If the directory that is the common path is also in the list, then care must be taken to not truncate it (as some other solutions on this page do). E.g. (/home/user1, /home/user1/foo, /home/user1/bar) should result in /home/user1, not /home.

package main

import (
	"fmt"
	"os"
	"path"
)

func CommonPrefix(sep byte, paths ...string) string {
	// Handle special cases.
	switch len(paths) {
	case 0:
		return ""
	case 1:
		return path.Clean(paths[0])
	}

	// Note, we treat string as []byte, not []rune as is often
	// done in Go. (And sep as byte, not rune). This is because
	// most/all supported OS' treat paths as string of non-zero
	// bytes. A filename may be displayed as a sequence of Unicode
	// runes (typically encoded as UTF-8) but paths are
	// not required to be valid UTF-8 or in any normalized form
	// (e.g. "é" (U+00C9) and "é" (U+0065,U+0301) are different
	// file names.
	c := []byte(path.Clean(paths[0]))

	// We add a trailing sep to handle the case where the
	// common prefix directory is included in the path list
	// (e.g. /home/user1, /home/user1/foo, /home/user1/bar).
	// path.Clean will have cleaned off trailing / separators with
	// the exception of the root directory, "/" (in which case we
	// make it "//", but this will get fixed up to "/" bellow).
	c = append(c, sep)

	// Ignore the first path since it's already in c
	for _, v := range paths[1:] {
		// Clean up each path before testing it
		v = path.Clean(v) + string(sep)

		// Find the first non-common byte and truncate c
		if len(v) < len(c) {
			c = c[:len(v)]
		}
		for i := 0; i < len(c); i++ {
			if v[i] != c[i] {
				c = c[:i]
				break
			}
		}
	}

	// Remove trailing non-separator characters and the final separator
	for i := len(c) - 1; i >= 0; i-- {
		if c[i] == sep {
			c = c[:i]
			break
		}
	}

	return string(c)
}

func main() {
	c := CommonPrefix(os.PathSeparator,
		//"/home/user1/tmp",
		"/home/user1/tmp/coverage/test",
		"/home/user1/tmp/covert/operator",
		"/home/user1/tmp/coven/members",
		"/home//user1/tmp/coventry",
		"/home/user1/././tmp/covertly/foo",
		"/home/bob/../user1/tmp/coved/bar",
	)
	if c == "" {
		fmt.Println("No common path")
	} else {
		fmt.Println("Common path:", c)
	}
}

Groovy

Solution:

def commonPath = { delim, Object[] paths ->
    def pathParts = paths.collect { it.split(delim) }
    pathParts.transpose().inject([match:true, commonParts:[]]) { aggregator, part ->
        aggregator.match = aggregator.match && part.every { it == part [0] }
        if (aggregator.match) { aggregator.commonParts << part[0] }
        aggregator
    }.commonParts.join(delim)
}

Test:

println commonPath('/',
    '/home/user1/tmp/coverage/test',
    '/home/user1/tmp/covert/operator',
    '/home/user1/tmp/coven/members')

println commonPath('/',
    '/home/user1/tmp/coverage/test',
    '/home/user1/tmp/covert/test',
    '/home/user1/tmp/coven/test',
    '/home/user1/tmp/covers/test')

Output:

/home/user1/tmp
/home/user1/tmp

GW-BASIC

Works with: GW-BASIC
Works with: Chipmunk Basic
Works with: QBasic

Because most BASICs don't have any sort of parsing functions built in, we have to deal with the entire string (rather than checking one level at a time).

Note that if the root directory is the common path, this reports the same as no match found (i.e. blank result).

10 REM All GOTO statements can be replaced with EXIT FOR in newer BASICs.

110 X$ = "/home/user1/tmp/coverage/test"
120 Y$ = "/home/user1/tmp/covert/operator"
130 Z$ = "/home/user1/tmp/coven/members"

150 A = LEN(X$)
160 IF A > LEN(Y$) THEN A = LEN(Y$)
170 IF A > LEN(Z$) THEN A = LEN(Z$)
180 FOR L0 = 1 TO A
190     IF MID$(X$, L0, 1) <> MID$(Y$, L0, 1) THEN GOTO 210
200 NEXT
210 A = L0 - 1

230 FOR L0 = 1 TO A
240     IF MID$(X$, L0, 1) <> MID$(Z$, L0, 1) THEN GOTO 260
250 NEXT
260 A = L0 - 1

280 IF MID$(X$, L0, 1) <> "/" THEN
290     FOR L0 = A TO 1 STEP -1
300        IF "/" = MID$(X$, L0, 1) THEN GOTO 340
310     NEXT
320 END IF

340 REM Task description says no trailing slash, so...
350 A = L0 - 1
360 P$ = LEFT$(X$, A)
370 PRINT "Common path is '"; P$; "'"

Output:

Common path is '/home/user1/tmp'

Haskell

import Data.List

-- Return the common prefix of two lists.
commonPrefix2 (x:xs) (y:ys) | x == y = x : commonPrefix2 xs ys
commonPrefix2 _ _ = []

-- Return the common prefix of zero or more lists.
commonPrefix (xs:xss) = foldr commonPrefix2 xs xss
commonPrefix _ = []

-- Split a string into path components.
splitPath = groupBy (\_ c -> c /= '/')

-- Return the common prefix of zero or more paths.
-- Note that '/' by itself is not considered a path component,
-- so "/" and "/foo" are treated as having nothing in common.
commonDirPath = concat . commonPrefix . map splitPath

main = putStrLn $ commonDirPath [
        "/home/user1/tmp/coverage/test",
        "/home/user1/tmp/covert/operator",
        "/home/user1/tmp/coven/members"
       ]

Or, expressed directly in applicative terms:

import Data.List (transpose, intercalate)
import Data.List.Split (splitOn)


------------------ COMMON DIRECTORY PATH -----------------

commonDirectoryPath :: [String] -> String
commonDirectoryPath [] = []
commonDirectoryPath xs =
  intercalate "/" $
  head <$> takeWhile ((all . (==) . head) <*> tail) $
  transpose (splitOn "/" <$> xs)

--------------------------- TEST -------------------------
main :: IO ()
main =
  (putStrLn . commonDirectoryPath)
    [ "/home/user1/tmp/coverage/test"
    , "/home/user1/tmp/covert/operator"
    , "/home/user1/tmp/coven/members"
    ]
Output:
/home/user1/tmp

HicEst

CHARACTER a='/home/user1/tmp/coverage/test', b='/home/user1/tmp/covert/operator', c='/home/user1/tmp/coven/members'

minLength = MIN( LEN(a), LEN(b), LEN(c) )
lastSlash = 0

DO i = 1, minLength
  IF( (a(i) == b(i)) * (b(i) == c(i)) ) THEN
    IF(a(i) == "/") lastSlash = i
  ELSEIF( lastSlash ) THEN
    WRITE(Messagebox) "Common Directory = ", a(1:lastSlash-1)
  ELSE
    WRITE(Messagebox, Name) "No common directory for", a, b, c
  ENDIF
ENDDO

Icon and Unicon

procedure main()
write(lcdsubstr(["/home/user1/tmp/coverage/test","/home/user1/tmp/covert/operator","/home/user1/tmp/coven/members"]))
end

procedure lcdsubstr(sL,d)   #: return the longest common sub-string of strings in the list sL delimited by d
local ss

/d := "/"
reverse(sL[1]) ? {
   if tab(find(d)+*d) || allmatch(ss := reverse(tab(0)),sL) then
      return ss
   }
end

procedure allmatch(s,L)   #: retrun s if it matches all strings in L
local x
every x := !L do
   if not match(s,x) then fail
return s
end

J

Solution:

parseDirs       =: = <;.2 ]
getCommonPrefix =: {. ;@{.~ 0 i.~ *./@(="1 {.)

getCommonDirPath=: [: getCommonPrefix parseDirs&>

Example:

   paths=: '/home/user1/tmp/coverage/test';'/home/user1/tmp/covert/operator';'/home/user1/tmp/coven/members'
   getCommonPrefix >paths
/home/user1/tmp/cove
   '/' getCommonDirPath paths
/home/user1/tmp/

Note: This alternative formulation of parseDirs provides cross-platform support, without the need to specify the path separator.

parseDirs       =: (PATHSEP_j_&= <;.2 ])@jhostpath

Java

Works with: Java version 1.5+

This example is case-sensitive.

public class CommonPath {
	public static String commonPath(String... paths){
		String commonPath = "";
		String[][] folders = new String[paths.length][];
		for(int i = 0; i < paths.length; i++){
			folders[i] = paths[i].split("/"); //split on file separator
		}
		for(int j = 0; j < folders[0].length; j++){
			String thisFolder = folders[0][j]; //grab the next folder name in the first path
			boolean allMatched = true; //assume all have matched in case there are no more paths
			for(int i = 1; i < folders.length && allMatched; i++){ //look at the other paths
				if(folders[i].length < j){ //if there is no folder here
					allMatched = false; //no match
					break; //stop looking because we've gone as far as we can
				}
				//otherwise
				allMatched &= folders[i][j].equals(thisFolder); //check if it matched
			}
			if(allMatched){ //if they all matched this folder name
				commonPath += thisFolder + "/"; //add it to the answer
			}else{//otherwise
				break;//stop looking
			}
		}
		return commonPath;
	}
	
	public static void main(String[] args){
		String[] paths = { "/home/user1/tmp/coverage/test",
				 "/home/user1/tmp/covert/operator",
				 "/home/user1/tmp/coven/members"};
		System.out.println(commonPath(paths));
		
		String[] paths2 = { "/hame/user1/tmp/coverage/test",
				 "/home/user1/tmp/covert/operator",
				 "/home/user1/tmp/coven/members"};
		System.out.println(commonPath(paths2));
	}
}

Output:

/home/user1/tmp/
/

Change folders[i] = paths[i].split("/"); to add more separators. Ex: to add "\" and "." as separators, change the line to folders[i] = paths[i].split("[/\\\\.]"); (adding square brackets makes the regex choose one character out of the group inside, adding all of the extra backslashes escapes the backslash character). After making this change, all separators will be changed to "/" in the result, but they can be mixed in the path (e.g. "/home.user1/tmp\\coverage/test" (escaped backslash) will act the same as "/home/user1/tmp/coverage/test").

A slightly modified version of the previous program, only the method commonPath() is changed.

	static String commonPath(String...  paths){
		String commonPath = "";
		String[][] folders = new String[paths.length][];
		
		for(int i=0; i<paths.length; i++){
			folders[i] = paths[i].split("/");
		}
			
		for(int j = 0; j< folders[0].length; j++){
			String s = folders[0][j];
			for(int i=1; i<paths.length; i++){
				if(!s.equals(folders[i][j]))
					return commonPath;
			}
			commonPath += s + "/";
		}
		return commonPath;		
	}

JavaScript

/**
 * Given an array of strings, return an array of arrays, containing the
 * strings split at the given separator
 * @param {!Array<!string>} a
 * @param {string} sep
 * @returns {!Array<!Array<string>>}
 */
const splitStrings = (a, sep = '/') => a.map(i => i.split(sep));

/**
 * Given an index number, return a function that takes an array and returns the
 * element at the given index
 * @param {number} i
 * @return {function(!Array<*>): *}
 */
const elAt = i => a => a[i];

/**
 * Transpose an array of arrays:
 * Example:
 * [['a', 'b', 'c'], ['A', 'B', 'C'], [1, 2, 3]] ->
 * [['a', 'A', 1], ['b', 'B', 2], ['c', 'C', 3]]
 * @param {!Array<!Array<*>>} a
 * @return {!Array<!Array<*>>}
 */
const rotate = a => a[0].map((e, i) => a.map(elAt(i)));

/**
 * Checks of all the elements in the array are the same.
 * @param {!Array<*>} arr
 * @return {boolean}
 */
const allElementsEqual = arr => arr.every(e => e === arr[0]);


const commonPath = (input, sep = '/') => rotate(splitStrings(input, sep))
    .filter(allElementsEqual).map(elAt(0)).join(sep);

const cdpInput = [
  '/home/user1/tmp/coverage/test',
  '/home/user1/tmp/covert/operator',
  '/home/user1/tmp/coven/members',
];

console.log(`Common path is: ${commonPath(cdpInput)}`);
Output:
Common path is: /home/user1/tmp

jq

# maximal_initial_subarray takes as input an array of arrays:
def maximal_initial_subarray:
  (map( .[0] ) | unique) as $u
  | if $u == [ null ] then []
    elif ($u|length) == 1
    then  $u  + ( map( .[1:] ) | maximal_initial_subarray)
    else []
    end ;

# Solution: read in the strings, convert to an array of arrays, and proceed:
def common_path(slash):
  [.[] | split(slash)] | maximal_initial_subarray | join(slash) ;

common_path("/")

Assuming the above jq program is in a file named common_path.jq and that the file directories.txt contains the three given directory strings quoted with double quotation marks:

$ jq -s -f common_path.jq directories.txt
"home/user1/tmp"

Julia

Works with: Julia version 0.6
function commonpath(ds::Vector{<:AbstractString}, dlm::Char='/')
    0 < length(ds) || return ""
    1 < length(ds) || return String(ds[1])
    p = split(ds[1], dlm)
    mincnt = length(p)
    for d in ds[2:end]
        q = split(d, dlm)
        mincnt = min(mincnt, length(q))
        hits = findfirst(p[1:mincnt] .!= q[1:mincnt])
        if hits != 0 mincnt = hits - 1 end
        if mincnt == 0 return "" end
    end
    1 < mincnt || p[1] != "" || return convert(T, string(dlm))
    return join(p[1:mincnt], dlm)
end

test = ["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"]

println("Comparing:\n - ", join(test, "\n - "))
println("for their common directory path yields:\n", commonpath(test))
Output:
Comparing:
 - /home/user1/tmp/coverage/test
 - /home/user1/tmp/covert/operator
 - /home/user1/tmp/coven/members
for their common directory path yields:
/home/user1/tmp

Kotlin

// version 1.1.51

fun findCommonDirPath(paths: List<String>, separator: Char): String {
    if (paths.isEmpty()) return ""
    if (paths.size == 1) return paths[0]
    val splits = paths[0].split(separator)
    val n = splits.size
    val paths2 = paths.drop(1)
    var k = 0
    var common = ""
    while (true) {
        val prevCommon = common
        common += if (k == 0) splits[0] else separator + splits[k]
        if (!paths2.all { it.startsWith(common + separator) || it == common } ) return prevCommon
        if (++k == n) return common
    }
}

fun main(args: Array<String>) {
    val paths = listOf(
        "/home/user1/tmp/coverage/test",
        "/home/user1/tmp/covert/operator",
        "/home/user1/tmp/coven/members"
    )
    val pathsToPrint = paths.map { "   '$it'" }.joinToString("\n")
    println("The common directory path of:\n\n$pathsToPrint\n")
    println("is '${findCommonDirPath(paths, '/')}'")
}
Output:
The common directory path of:

   '/home/user1/tmp/coverage/test'
   '/home/user1/tmp/covert/operator'
   '/home/user1/tmp/coven/members'

is '/home/user1/tmp'

Lasso

#!/usr/bin/lasso9

local(
	path1 = '/home/user1/tmp/coverage/test' -> split('/'),
	path2 = '/home/user1/tmp/covert/operator' -> split('/'),
	path3 = '/home/user1/tmp/coven/members' -> split('/')
)

define commonpath(...) => {
	local(shared = #rest -> get(1))
	loop(#rest -> size - 1) => {
		#shared = #shared -> intersection(#rest -> get(loop_count + 1))
	}
	return #shared -> join('/')
}

stdoutnl(commonpath(#path1, #path2, #path3))

Output:

/home/user1/tmp

Liberty BASIC

path$(1) = "/home/user1/tmp/coverage/test"
path$(2) = "/home/user1/tmp/covert/operator"
path$(3) = "/home/user1/tmp/coven/members"


print samepath$(3,"/")
end

function samepath$(paths,sep$)
    d = 1 'directory depth
    n = 2 'path$(number)
    while 1
        if word$(path$(1),d,sep$) <> word$(path$(n),d,sep$) or word$(path$(1),d,sep$) = "" then exit while
        n = n + 1
        if n > paths then
            if right$(samepath$,1) <> sep$ and d<>1 then
                samepath$ = samepath$ + sep$ + word$(path$(1),d,sep$)
            else
                samepath$ = samepath$ + word$(path$(1),d,sep$)
            end if
            n = 2
            d = d + 1
        end if
    wend
end function

Lingo

on getCommonPath (pathes, sep)
  _player.itemDelimiter = sep

  -- find length of shortest path (in terms of items)
  commonCnt = the maxInteger
  repeat with p in pathes
    if p.item.count<commonCnt then commonCnt=p.item.count
  end repeat

  pathCnt = pathes.count
  repeat with i = 1 to commonCnt
    repeat with j = 2 to pathCnt
      if pathes[j].item[i]<>pathes[j-1].item[i] then
        return pathes[1].item[1..i-1]
      end if
    end repeat
  end repeat
  return pathes[1].item[1..commonCnt]
end
pathes = []
pathes.add("/home/user1/tmp/coverage/test")
pathes.add("/home/user1/tmp/covert/operator")
pathes.add("/home/user1/tmp/coven/members")

put getCommonPath(pathes, "/")
-- "/home/user1/tmp"

MapBasic

Derived from the BASIC example above

Include "MapBasic.def"

Declare Sub Main
DECLARE FUNCTION commonPath(paths() AS STRING, ByVal pathSep AS STRING) as String

FUNCTION commonPath(paths() AS STRING, ByVal pathSep AS STRING) as String
    DIM tmpint1 AS INTEGER, tmpint2 AS INTEGER, tmpstr1 AS STRING, tmpstr2 AS STRING
    DIM L0 AS INTEGER, L1 AS INTEGER, lowerbound AS INTEGER, upperbound AS INTEGER
    
    lowerbound = 1
    upperbound = UBOUND(paths)
 
    IF (lowerbound) = upperbound THEN       'Some quick error checking...
        commonPath = paths(lowerbound)
    ELSEIF lowerbound > upperbound THEN     'How in the...?
        commonPath = ""
    ELSE
        tmpstr1 = paths(lowerbound)
 
        FOR L0 = (lowerbound) TO upperbound 'Find common strings.
            tmpstr2 = paths(L0)
            tmpint1 = LEN(tmpstr1)
            tmpint2 = LEN(tmpstr2)
            IF tmpint1 > tmpint2 
            THEN tmpint1 = tmpint2

            FOR L1 = 1 TO tmpint1
                IF MID$(tmpstr1, L1, 1) <> MID$(tmpstr2, L1, 1) THEN
                    tmpint1 = L1 - 1
                    EXIT FOR
                END IF
            NEXT
                      end If 
           tmpstr1 = LEFT$(tmpstr1, tmpint1)
        NEXT
 
        IF RIGHT$(tmpstr1, 1) <> pathSep THEN
            FOR L1 = tmpint1 TO 2 STEP -1
                IF (pathSep) = MID$(tmpstr1, L1, 1) THEN
                    tmpstr1 = LEFT$(tmpstr1, L1 - 1)
                    EXIT FOR
                END IF
            NEXT  
            IF LEN(tmpstr1) = tmpint1 
            THEN tmpstr1 = ""
       		 ELSEIF tmpint1 > 1 
       		 THEN
            tmpstr1 = LEFT$(tmpstr1, tmpint1 - 1)
        	END IF
 		 end if  
        commonPath = tmpstr1
    END IF
END FUNCTION

Sub Main()
	Dim x(3) as  String
	Define Sep "/"
	
	x(1) = "/home/user1/tmp/"
	x(2) = "/home/user1/tmp/covert/operator"
	x(3) = "/home/user1/tmp/coven/members"
 

PRINT "Common path is " + commonPath(x(), Sep)

End Sub

Maple

dirpath:=proc(a,b,c)
	local dirtemp,dirnew,x;
	use StringTools in
		dirtemp:=LongestCommonSubString(c, LongestCommonSubString(a,b));
		x:=FirstFromRight("/",dirtemp);
		dirnew:=dirtemp[1..x];
	return dirnew;
	end use;
end proc;

Mathematica/Wolfram Language

FindCommonDirectory[x_] := If[StringTake[#, -1] != "/", StringTake[#, Max[StringPosition[#, "/"]]], #] &
 [Fold[LongestCommonSubsequence, First[x] , Rest[x]]]

FindCommonDirectory[{"/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"}]
->"/home/user1/tmp/"

MATLAB / Octave

function lcp = longest_common_dirpath(varargin)
ix    = find(varargin{1}=='/');
ca    = char(varargin);
flag  = all(ca==ca(1,:),1);
for k = length(ix):-1:1,
	if all(flag(1:ix(k))); break; end 
end
lcp = ca(1,1:ix(k));
end

longest_common_dirpath('/home/user1/tmp/coverage/test', '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members')
Output:
ans = /home/user1/tmp/

Maxima

scommon(a, b) := block([n: min(slength(a), slength(b))],
   substring(a, 1, catch(for i thru n do (
   if not cequal(charat(a, i), charat(b, i)) then throw(i)), n + 1)))$

commonpath(u, [l]) := block([s: lreduce(scommon, u), c, n],
   n: sposition(if length(l) = 0 then "/" else l[1], sreverse(s)),
   if integerp(n) then substring(s, 1, slength(s) - n) else ""
)$

commonpath(["c:/files/banister.jpg", "c:/files/bank.xls", "c:/files/banana-recipes.txt"]);
"c:/files"

MUMPS

FCD
 NEW D,SEP,EQ,LONG,DONE,I,J,K,RETURN
 SET D(1)="/home/user1/tmp/coverage/test"
 SET D(2)="/home/user1/tmp/covert/operator"
 SET D(3)="/home/user1/tmp/coven/members"
 SET SEP="/"
 SET LONG=D(1)
 SET DONE=0
 FOR I=1:1:$LENGTH(LONG,SEP) QUIT:DONE  SET EQ(I)=1 FOR J=2:1:3 SET EQ(I)=($PIECE(D(J),SEP,I)=$PIECE(LONG,SEP,I))&EQ(I) SET DONE='EQ(I) QUIT:'EQ(I)
 SET RETURN=""
 FOR K=1:1:I-1 Q:'EQ(K)  SET:EQ(K) $PIECE(RETURN,SEP,K)=$PIECE(LONG,SEP,K)
 WRITE !,"For the paths:" FOR I=1:1:3 WRITE !,D(I)
 WRITE !,"The longest common directory is: ",RETURN
 KILL D,SEP,EQ,LONG,DONE,I,J,K,RETURN
 QUIT

Usage:

USER>D FCD^ROSETTA
 
For the paths:
/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members
The longest common directory is: /home/user1/tmp

Nim

import strutils

proc commonprefix(paths: openarray[string], sep = "/"): string =
  if paths.len == 0: return ""
  block outer:
    for i in 0 ..< paths[0].len:
      result = paths[0][0 .. i]
      for path in paths:
        if not path.startsWith(result):
          break outer
  result = result[0 .. result.rfind(sep)]

echo commonprefix(@["/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"])

Output:

/home/user1/tmp

OCaml

let rec common_prefix xs ys = match xs, ys with
  | x :: xs, y :: ys when x = y -> x :: common_prefix xs ys
  | _ -> []

let common_prefix_all = function
  | x :: xs -> List.fold_left common_prefix x xs
  | _ -> []

let common_ancestor ~sep paths =
  List.map Str.(split_delim (regexp_string sep)) paths
  |> common_prefix_all
  |> String.concat sep

let _ = assert begin
  common_ancestor ~sep:"/" [
    "/home/user1/tmp/coverage/test";
    "/home/user1/tmp/covert/operator";
    "/home/user1/tmp/coven/members";
  ] = "/home/user1/tmp"
end

Requires the standard str library

OpenEdge/Progress

FUNCTION findCommonDir RETURNS CHAR(
   i_cdirs        AS CHAR,
   i_cseparator   AS CHAR
):

   DEF VAR idir      AS INT.
   DEF VAR idepth    AS INT. 
   DEF VAR cdir      AS CHAR EXTENT.
   DEF VAR lsame     AS LOGICAL INITIAL TRUE.
   DEF VAR cresult   AS CHAR.

   EXTENT( cdir ) = NUM-ENTRIES( i_cdirs, '~n' ).

   DO idir = 1 TO NUM-ENTRIES( i_cdirs, '~n' ):
      cdir[ idir ] = ENTRY( idir, i_cdirs, '~n' ).
   END.

   DO idepth = 2 TO NUM-ENTRIES( cdir [ 1 ], i_cseparator ) WHILE lsame:
      DO idir = 1 TO EXTENT( cdir ) - 1 WHILE lsame:
         lsame =  ( 
                     ENTRY( idepth, cdir [ idir ], i_cseparator ) = 
                     ENTRY( idepth, cdir [ idir + 1 ], i_cseparator )
                  ).
      END.
      IF lsame THEN
         cresult = cresult + i_cseparator + ENTRY( idepth, cdir [ 1 ], i_cseparator ).
   END.

   RETURN cresult.

END FUNCTION.
MESSAGE 
   findCommonDir(
      '/home/user1/tmp/coverage/test' + '~n' +
      '/home/user1/tmp/covert/operator' + '~n' +
      '/home/user1/tmp/coven/members',
      '/'
   )
VIEW-AS ALERT-BOX

Output

---------------------------
Message (Press HELP to view stack trace)
---------------------------
/home/user1/tmp
---------------------------
OK   Help   
---------------------------

Oz

With a few helper functions, we can express the solution like this in Oz:

declare
  fun {CommonPrefix Sep Paths}
     fun {GetParts P} {String.tokens P Sep} end
     Parts = {ZipN {Map Paths GetParts}}
     EqualParts = {List.takeWhile Parts fun {$ X|Xr} {All Xr {Equals X}} end}
  in
     {Join Sep {Map EqualParts Head}}
  end

  fun {ZipN Xs}
     if {Some Xs {Equals nil}} then nil
     else
        {Map Xs Head} | {ZipN {Map Xs Tail}}
     end
  end

  fun {Join Sep Xs}
     {FoldR Xs fun {$ X Z} {Append X Sep|Z} end nil}
  end

  fun {Equals C}
     fun {$ X} X == C end
  end

  fun {Head X|_} X end

  fun {Tail _|Xr} Xr end
in
  {System.showInfo {CommonPrefix &/
                    ["/home/user1/tmp/coverage/test"
                     "/home/user1/tmp/covert/operator"
                     "/home/user1/tmp/coven/members"]}}

PARI/GP

cdp(v)={
  my(s="");
  v=apply(t->Vec(t),v);
  for(i=1,vecmin(apply(length,v)),
    for(j=2,#v,
      if(v[j][i]!=v[1][i],return(s)));
      if(i>1&v[1][i]=="/",s=concat(vecextract(v[1],1<<(i-1)-1))
    )
  );
  if(vecmax(apply(length,v))==vecmin(apply(length,v)),concat(v[1]),s)
};
cdp(["/home/user1/tmp/coverage/test","/home/user1/tmp/covert/operator","/home/user1/tmp/coven/members"])

Pascal

Free Pascal

Program CommonPaths;
{$mode ObjFPC}{$H+}
uses
  Classes, Math;

const
  Paths: array of string = ('/home/user1/tmp/coverage/test',
                            '/home/user1/tmp/covert/operator',
                            '/home/user1/tmp/coven/members');

function FindShortestCommonPath(arr: array of TStringList; shortestPath: Integer): string;
var
  i, j: Integer;
  commonStr: string;
begin
  Result := '/';
  if Length(arr) = 0 then
    Exit;
  for j := 0 to shortestPath - 1 do
  begin
    commonStr := arr[0][j];
    for i := 1 to High(arr) do
    begin
      if arr[i][j] <> commonStr then
        Exit(Result);
    end;
    Result := Result + commonStr + '/';
  end;
end;
var
  arr: array of TStringList;
  i, shortestpath: uint32;

begin
  shortestpath := High(uint32);
  SetLength(arr, Length(paths));

  for i := 0 to High(paths) do
  begin
    arr[i] := TStringList.Create;
    arr[i].AddDelimitedText(paths[i], '/', false);
    arr[i].Delete(0);
    shortestpath := Min(shortestpath, arr[i].Count);
  end;

  Writeln(FindShortestCommonPath(arr, shortestpath));

  for i := 0 to High(paths) do
    arr[i].Free;
end.
Output:
/home/user1/tmp/



Perl

A solution which lets the regex engine do all the work (it operates on the concatenation of the given paths delimited by null-bytes, which should be safe since null-bytes are not allowed inside paths):

sub common_prefix {
    my $sep = shift;
    my $paths = join "\0", map { $_.$sep } @_;
    $paths =~ /^ ( [^\0]* ) $sep [^\0]* (?: \0 \1 $sep [^\0]* )* $/x;
    return $1;
}

A more conventional solution, which tallies up all potential prefixes from the given paths and then looks for the longest one that occurred the same number of times as there are paths:

use List::Util qw(first);

sub common_prefix {
    my ($sep, @paths) = @_;
    my %prefixes;
    
    for (@paths) {
        do { ++$prefixes{$_} } while s/$sep [^$sep]* $//x
    }
    
    return first { $prefixes{$_} == @paths } reverse sort keys %prefixes;
}

Testing:

my @paths = qw(/home/user1/tmp/coverage/test 
               /home/user1/tmp/covert/operator
               /home/user1/tmp/coven/members);
print common_prefix('/', @paths), "\n";
Output:
/home/user1/tmp

Phix

Note: if the testset contains /home/user1/tmp the result is /home/user1, with /home/user1/tmp/ instead of that, it is /home/user1/tmp
To change that behaviour, simply remove both the [1..-2]
For cross-platform operation, simply use the split_path and join_path builtins (not pwa/p2js compatible) instead of split and join.

with javascript_semantics
function common_directory_path(sequence paths, integer sep='/')
    sequence res = {}
    if length(paths) then
        res = split(paths[1],sep)[1..-2]
        for i=2 to length(paths) do
            sequence pi = split(paths[i],sep)[1..-2]
            for j=1 to length(res) do
                if j>length(pi) or res[j]!=pi[j] then
                    res = res[1..j-1]
                    exit
                end if
            end for
            if length(res)=0 then exit end if
        end for
    end if
    return join(res,sep)
end function
 
constant test = {"/home/user1/tmp/coverage/test",
                 "/home/user1/tmp/covert/operator",
                 "/home/user1/tmp/coven/members"}
?common_directory_path(test)
Output:
"home/user1/tmp"

PHP

<?php

/*
 This works with dirs and files in any number of combinations.
*/

function _commonPath($dirList)
{
	$arr = array();
	foreach($dirList as $i => $path)
	{
		$dirList[$i]	= explode('/', $path);
		unset($dirList[$i][0]);
		
		$arr[$i] = count($dirList[$i]);
	}
	
	$min = min($arr);
	
	for($i = 0; $i < count($dirList); $i++)
	{
		while(count($dirList[$i]) > $min)
		{
			array_pop($dirList[$i]);
		}
		
		$dirList[$i] = '/' . implode('/' , $dirList[$i]);
	}
	
	$dirList = array_unique($dirList);
	while(count($dirList) !== 1)
	{
		$dirList = array_map('dirname', $dirList);
		$dirList = array_unique($dirList);
	}
	reset($dirList);
	
	return current($dirList);
}

 /* TEST */

$dirs = array(
 '/home/user1/tmp/coverage/test',
 '/home/user1/tmp/covert/operator',
 '/home/user1/tmp/coven/members',
);


if('/home/user1/tmp' !== common_path($dirs))
{
  echo 'test fail';
} else {
  echo 'test success';
}

?>


<?php

/* A more compact string-only version, which I assume would be much faster */
/* If you want the trailing /, return $common; */

function getCommonPath($paths) {
	$lastOffset = 1;
	$common = '/';
	while (($index = strpos($paths[0], '/', $lastOffset)) !== FALSE) {
		$dirLen = $index - $lastOffset + 1;	// include /
		$dir = substr($paths[0], $lastOffset, $dirLen);
		foreach ($paths as $path) {
			if (substr($path, $lastOffset, $dirLen) != $dir)
				return $common;
		}
		$common .= $dir;
		$lastOffset = $index + 1;
	}
	return substr($common, 0, -1);
}

?>

Picat

Here are two different approaches. Both use append/3 for getting the common prefix.

Using maxof/2

Using maxof/2 to get the longest common prefix.

find_common_directory_path(Dirs) = Path =>
  maxof( (common_prefix(Dirs,  Path,Len), append(_,"/",Path)), Len).

%
% Find a common prefix of all lists/strings in Ls.
% Using append/3.
% 
common_prefix(Ls, Prefix,Len) => 
  foreach(L in Ls) 
    append(Prefix,_,L)
  end,
  Len = Prefix.length.

Mode-directed tabling

Using mode-directed tabling for maximizing the length.

find_common_directory_path2(Dirs) = Path =>
  common_prefix2(Dirs,Path,'/',_Len).

table(+,-,max)
common_prefix2(Ls,Prefix,Len) => 
  common_prefix2(Ls,Prefix,[],Len).

table(+,-,+,max)
common_prefix2(Ls,Prefix,Last,Len) =>
  foreach(L in Ls) 
    append(Prefix,_,L)
  end,
  if Last != [], Prefix != [] then
    Prefix.last() == Last
  end,
  Len = Prefix.length.

For the directories

  Dirs = [
          "/home/user1/tmp/coverage/test",
          "/home/user1/tmp/covert/operator",
          "/home/user1/tmp/coven/members"
          ]

both find this solution:

path = /home/user1/tmp/

PicoLisp

(de commonPath (Lst Chr)
   (glue Chr
      (make
         (apply find
            (mapcar '((L) (split (chop L) Chr)) Lst)
            '(@ (or (pass <>) (nil (link (next))))) ) ) ) )

Output:

(commonPath
   (quote
      "/home/user1/tmp/coverage/test"
      "/home/user1/tmp/covert/operator"
      "/home/user1/tmp/coven/members" )
   "/" )

-> "/home/user1/tmp"

Pike

array paths = ({ "/home/user1/tmp/coverage/test",
                 "/home/user1/tmp/covert/operator",
                 "/home/user1/tmp/coven/members" });

// append a / to each entry, so that a path like "/home/user1/tmp" will be recognized as a prefix
// without it the prefix would end up being "/home/user1/"
paths = paths[*]+"/";

string cp = String.common_prefix(paths);
cp = cp[..sizeof(cp)-search(reverse(cp), "/")-2];
Result: "/home/user1/tmp"

PowerBASIC

Translation of: Visual Basic
#COMPILE EXE
#DIM ALL
#COMPILER PBCC 6
$PATH_SEPARATOR = "/"

FUNCTION CommonDirectoryPath(Paths() AS STRING) AS STRING
LOCAL s AS STRING
LOCAL i, j, k AS LONG
  k = 1
  DO
    FOR i = 0 TO UBOUND(Paths)
      IF i THEN
        IF INSTR(k, Paths(i), $PATH_SEPARATOR) <> j THEN
          EXIT DO
        ELSEIF LEFT$(Paths(i), j) <> LEFT$(Paths(0), j) THEN
          EXIT DO
        END IF
      ELSE
        j = INSTR(k, Paths(i), $PATH_SEPARATOR)
        IF j = 0 THEN
          EXIT DO
        END IF
      END IF
    NEXT i
    s = LEFT$(Paths(0), j + CLNG(k <> 1))
    k = j + 1
  LOOP
  FUNCTION = s

END FUNCTION


FUNCTION PBMAIN () AS LONG

' testing the above function

LOCAL s() AS STRING
LOCAL i AS LONG

REDIM s(0 TO 2)
ARRAY ASSIGN s() = "/home/user1/tmp/coverage/test", _
                   "/home/user1/tmp/covert/operator", _
                   "/home/user1/tmp/coven/members"
FOR i = 0 TO UBOUND(s()): CON.PRINT s(i): NEXT i
CON.PRINT CommonDirectoryPath(s()) & " <- common"
CON.PRINT

REDIM s(0 TO 3)
ARRAY ASSIGN s() = "/home/user1/tmp/coverage/test", _
                   "/home/user1/tmp/covert/operator", _
                   "/home/user1/tmp/coven/members", _
                   "/home/user1/abc/coven/members"
FOR i = 0 TO UBOUND(s()): CON.PRINT s(i): NEXT i
CON.PRINT CommonDirectoryPath(s()) & " <- common"
CON.PRINT

REDIM s(0 TO 2)
ARRAY ASSIGN s() = "/home/user1/tmp/coverage/test", _
                   "/hope/user1/tmp/covert/operator", _
                   "/home/user1/tmp/coven/members"
FOR i = 0 TO UBOUND(s()): CON.PRINT s(i): NEXT i
CON.PRINT CommonDirectoryPath(s()) & " <- common"
CON.PRINT

CON.PRINT "hit any key to end program"
CON.WAITKEY$

END FUNCTION
Output:
/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members
/home/user1/tmp <- common

/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members
/home/user1/abc/coven/members
/home/user1 <- common

/home/user1/tmp/coverage/test
/hope/user1/tmp/covert/operator
/home/user1/tmp/coven/members
/ <- common

PowerShell

<#
.Synopsis
    Finds the deepest common directory path of files passed through the pipeline.
.Parameter File
    PowerShell file object.
#>
function Get-CommonPath {
[CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [System.IO.FileInfo] $File
    )
    process {
        # Get the current file's path list
        $PathList =  $File.FullName -split "\$([IO.Path]::DirectorySeparatorChar)"
        # Get the most common path list
        if ($CommonPathList) {
            $CommonPathList = (Compare-Object -ReferenceObject $CommonPathList -DifferenceObject $PathList -IncludeEqual `
                -ExcludeDifferent -SyncWindow 0).InputObject
        } else {
            $CommonPathList = $PathList
        }
    }
    end {
        $CommonPathList -join [IO.Path]::DirectorySeparatorChar
    }
}

Sample execution:

"C:\a\b\c\d\e","C:\a\b\e\f","C:\a\b\c\d\x" | Get-CommonPath
C:\a\b

Prolog

%! directory_prefix(PATHs,STOP0,PREFIX)

directory_prefix([],_STOP0_,'')
:-
!
.

directory_prefix(PATHs0,STOP0,PREFIX)
:-
prolog:once(longest_prefix(PATHs0,STOP0,LONGEST_PREFIX)) ->
prolog:atom_concat(PREFIX,STOP0,LONGEST_PREFIX) ;
PREFIX=''
. 

%! longest_prefix(PATHs,STOP0,PREFIX)

longest_prefix(PATHs0,STOP0,PREFIX)
:-
QUERY=(shortest_prefix(PATHs0,STOP0,SHORTEST_PREFIX)) ,
prolog:findall(SHORTEST_PREFIX,QUERY,SHORTEST_PREFIXs) ,
lists:reverse(SHORTEST_PREFIXs,LONGEST_PREFIXs) ,
lists:member(PREFIX,LONGEST_PREFIXs)
.

%! shortest_prefix(PATHs,STOP0,PREFIX)

shortest_prefix([],_STOP0_,_PREFIX_) .

shortest_prefix([PATH0|PATHs0],STOP0,PREFIX)
:-
starts_with(PATH0,PREFIX) ,
ends_with(PREFIX,STOP0) ,
shortest_prefix(PATHs0,STOP0,PREFIX)
.

%! starts_with(TARGET,START)

starts_with(TARGET,START)
:-
prolog:atom_concat(START,_,TARGET)
.

%! ends_with(TARGET,END)

ends_with(TARGET,END)
:-
prolog:atom_concat(_,END,TARGET)
.
Output:
?- directory_prefix(['/home/user1/tmp/coverage/test','/home/user1/tmp/convert/operator','/home/user1/tmp/coven/members'],'/',PREFIX) .
PREFIX = '/home/user1/tmp' .

PureBasic

PureBasic don't have a path comparator directly but instead have powerful string tools.

Simply by checking the catalog names until they mismatch and add up the correct parts, the task is accomplished.

Procedure.s CommonPath(Array InPaths.s(1),separator.s="/")
  Protected SOut$=""
  Protected i, j, toggle
  
  If ArraySize(InPaths())=0
    ProcedureReturn InPaths(0)  ; Special case, only one path
  EndIf
  
  Repeat
    i+1
    toggle=#False
    For j=1 To ArraySize(InPaths())
      If (StringField(InPaths(j-1),i,separator)=StringField(InPaths(j),i,separator))
        If Not toggle
          SOut$+StringField(InPaths(j-1),i,separator)+separator
          toggle=#True
        EndIf
      Else
        ProcedureReturn SOut$
      EndIf      
    Next
  ForEver
EndProcedure

Example of implementation

Dim t.s(2)
t(0)="/home/user1/tmp/coverage/test"
t(1)="/home/user1/tmp/covert/operator"
t(2)="/home/user1/tmp/coven/members"

Debug CommonPath(t(),"/"))

Python

Since Python 3.5 os.path.commonpath function can be used:

>>> import os
>>> os.path.commonpath(['/home/user1/tmp/coverage/test', 
                        '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp'

The Python os.path.commonprefix function is broken as it returns common characters that may not form a valid directory path:

>>> import os
>>> os.path.commonprefix(['/home/user1/tmp/coverage/test', 
                          '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp/cove'

This result can be fixed:

>>> def commonprefix(args, sep='/'):
	return os.path.commonprefix(args).rpartition(sep)[0]

>>> commonprefix(['/home/user1/tmp/coverage/test', 
                  '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp'

Even shorter:

>>> paths = ['/home/user1/tmp/coverage/test', '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members']
>>> os.path.dirname(os.path.commonprefix(paths))
'/home/user1/tmp'

But it may be better to not rely on the faulty implementation at all:

>>> from itertools import takewhile
>>> def allnamesequal(name):
	return all(n==name[0] for n in name[1:])

>>> def commonprefix(paths, sep='/'):
	bydirectorylevels = zip(*[p.split(sep) for p in paths])
	return sep.join(x[0] for x in takewhile(allnamesequal, bydirectorylevels))

>>> commonprefix(['/home/user1/tmp/coverage/test', 
                  '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp'
>>> # And also
>>> commonprefix(['/home/user1/tmp', '/home/user1/tmp/coverage/test',
                  '/home/user1/tmp/covert/operator', '/home/user1/tmp/coven/members'])
'/home/user1/tmp'
>>>

Quackery

  [ over size
    over size min
    dup dip unrot times
      [ over i^ peek
        over i^ peek
        != if
          [ rot drop
            i^ unrot
            conclude ] ]
    2drop ]              is equalto ( $ $ --> n )

  [ 0 unrot over size
    times
      [ over i peek
        over = if
         [ rot drop
           i unrot
           conclude ] ]
    2drop ]             is lastof  ( $ c --> n )

  [ swap behead swap
    witheach
      [ over equalto
        split drop ]
    dup rot lastof
    split drop ]        is cdp     ( [ c --> n )

  $ '/home/user1/tmp/coverage/test
     /home/user1/tmp/covert/operator
     /home/user1/tmp/coven/members'  nest$
  char / cdp echo$
Output:
/home/user1/tmp

R

get_common_dir <- function(paths, delim = "/")
{
  path_chunks <- strsplit(paths, delim)
  
  i <- 1
  repeat({
    current_chunk <- sapply(path_chunks, function(x) x[i])
    if(any(current_chunk != current_chunk[1])) break
    i <- i + 1
  })
  paste(path_chunks[[1]][seq_len(i - 1)], collapse = delim)

}

# Example Usage:
paths <- c(
  '/home/user1/tmp/coverage/test',
  '/home/user1/tmp/covert/operator',
  '/home/user1/tmp/coven/members')

get_common_dir(paths)           # "/home/user1/tmp"

Racket

#lang racket

(define (common-directory path . paths)
  (string-join
   (let loop ([path  (string-split path "/" #:trim? #f)]
              [paths (map (λ(p) (string-split p "/" #:trim? #f)) paths)])
     (if (and (pair? path)
              (andmap (λ(p) (and (pair? p) (equal? (car p) (car path))))
                      paths))
       (cons (car path) (loop (cdr path) (map cdr paths)))
       '()))
   "/"))

(common-directory
 "/home/user1/tmp/coverage/test"
 "/home/user1/tmp/covert/operator"
 "/home/user1/tmp/coven/members")
;; --> "/home/user1/tmp"

Raku

(formerly Perl 6)

my $sep = '/';
my @dirs = </home/user1/tmp/coverage/test
            /home/user1/tmp/covert/operator
            /home/user1/tmp/coven/members>;

my @comps = @dirs.map: { [ .comb(/ $sep [ <!before $sep> . ]* /) ] }; 

my $prefix = '';

while all(@comps[*]»[0]) eq @comps[0][0] {
    $prefix ~= @comps[0][0] // last;
    @comps».shift;
}

say "The longest common path is $prefix";

Output:

The longest common path is /home/user1/tmp

If you'd prefer a pure FP solution without side effects, you can use this:

my $sep := '/';
my @dirs := </home/user1/tmp/coverage/test
             /home/user1/tmp/covert/operator
             /home/user1/tmp/coven/members>;

my @comps = @dirs.map: { [ .comb(/ $sep [ <!before $sep> . ]* /) ] };

say "The longest common path is ",
    gather for 0..* -> $column {
        last unless all(@comps[*]»[$column]) eq @comps[0][$column];
        take @comps[0][$column] // last;
    }

Or here's another factoring, that focuses on building the result with cumulative sequences and getting the solution with `first`:

my $sep = '/';
my @dirs = </home/user1/tmp/coverage/test
            /home/user1/tmp/covert/operator
            /home/user1/tmp/coven/members>;

sub is_common_prefix { so $^prefix eq all(@dirs).substr(0, $prefix.chars) }

say ([\~] @dirs.comb(/ $sep [ <!before $sep> . ]* /)).reverse.first: &is_common_prefix

REXX

/*REXX program  finds  the  common directory path  for a list of files.       */
/* original code: Gerard Schildberger                                         */
/* 20230606 Walter Pachl refurbisher adn improved (file.4 = 'home' -> /)      */
file.  = ''                            /*the default for all file lists (null)*/
file.1 = '/home/user1/tmp/coverage/test'
        /*123456789.123456789.123456768*/
file.2 = '/home/user1/tmp/covert/operator'
file.3 = '/home/user1/tmp/coven/members'

L=length(file.1)                       /*use the length of the first string.  */
Do j=2 While file.j\==''               /*loop for the other file names        */
  diffp=compare(file.j,file.1)         /*find the first different character   */
  If diffp>0 Then Do                   /*Strings are different                */
    L=min(L,diffp)                     /*get the minimum length equal strings.*/
    If right(file.j,1)<>'/' Then Do    /*not a directory                      */
     L=lastpos('/',left(file.j,L))     /* go back to directory end            */
      If L=0 Then Do
        Say 'common directory path: /'
        Exit
        End
      End
    End
  End
common=left(file.1,lastpos('/',file.1,L)) /*determine the shortest DIR string.*/
If right(common,1)=='/' Then           /* remove the trailing /               */
  common=left(common,length(common)-1)
If common=='' then common= "/"         /*if no common directory, assume home. */
Say 'common directory path: 'common
                                       /*stick a fork in it,  we're all done. */
output   when using the default inputs:
common directory path:  /home/user1/tmp

Ring

# Project : Find common directory path

load "stdlib.ring"
i = null
o = null
path = list(3)
 
path[1] = "/home/user1/tmp/coverage/test"
path[2] = "/home/user1/tmp/covert/operator"
path[3] = "/home/user1/tmp/coven/members"
 
see commonpath(path, "/")
 
func commonpath(p, s)
while i != 0
      o = i
      i = substring(p[1], s, i+1)
      for j = 2 to len(p)
          if left(p[1], i) != left(p[j], i) 
             exit 2
          ok
      next
end
return left(p[1], o-1)

Output:

/home/user1/tmp

RPL

Works with: HP version 48
≪ DUP SIZE → paths n
  ≪ paths n GET "/" +
     WHILE 'n' DECR REPEAT
        paths n GET "/" +
        DUP2 SIZE SWAP SIZE MIN DUP
        1 SWAP FOR j
           DROP
           OVER j DUP SUB OVER j DUP SUB ≠ 
           j
           IF SWAP THEN DUP 1 ≠ - OVER SIZE 'j' STO END
        NEXT
        1 SWAP SUB SWAP DROP
     END
     DUP SIZE
     WHILE DUP2 DUP SUB "/" ≠ REPEAT 1 - END
     DUP 1 ≠ - 
     1 SWAP SUB
 ≫ ≫ 'CPATH' STO
{ "/home/user1/tmp/coverage/test" "/home/user1/tmp/covert/operator" "/home/user1/tmp/coven/members" } CPATH
Output:
1: "/home/user1/tmp"

Ruby

Uses the standard library abbrev module: Given a set of strings, calculate the set of unambiguous abbreviations for those strings, and return a hash where the keys are all the possible abbreviations and the values are the full strings.

require 'abbrev'

dirs = %w( /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members )

common_prefix = dirs.abbrev.keys.min_by {|key| key.length}.chop  # => "/home/user1/tmp/cove"
common_directory = common_prefix.sub(%r{/[^/]*$}, '')            # => "/home/user1/tmp"

Implementing without that module:

separator = '/'
path0, *paths = dirs.collect {|dir| dir.split(separator)}
uncommon_idx = path0.zip(*paths).index {|dirnames| dirnames.uniq.length > 1}
uncommon_idx = path0.length  unless uncommon_idx                # if uncommon_idx==nil
common_directory = path0[0...uncommon_idx].join(separator)      # => "/home/user1/tmp"

or method version

def common_directory_path(dirs, separator='/')
  dir1, dir2 = dirs.minmax.map{|dir| dir.split(separator)}
  dir1.zip(dir2).take_while{|dn1,dn2| dn1==dn2}.map(&:first).join(separator)
end

p common_directory_path(dirs)           #=> "/home/user1/tmp"

Run BASIC

' ------------------------------------------
' Find common directory to all directories
' and directories common with other Paths
' ------------------------------------------
print word$(word$(httpget$("http://tycho.usno.navy.mil/cgi-bin/timer.pl"),1,"UTC"),2,"<BR>") ' Universal time

dim path$(20)
path$(1)	= "/home/user1/tmp/coverage/test"
path$(2)	= "/home/user1/tmp/covert/operator"
path$(3)	= "/home/user1/tmp/coven/members"

path$(4)	= "/home/user1/tmp1/coverage/test"
path$(5)	= "/home/user1/tmp1/covert/operator"
path$(6)	= "/home/user1/tmp1/coven/members"

path$(7)	= "/home/user1/tmp2/coverage/test"
path$(8)	= "/home/user1/tmp2/covert/operator"
path$(9)	= "/home/user1/tmp2/coven/members"

path$(10)	= "/home/user1/tmp3/coverage/test"
path$(11)	= "/home/user1/tmp3/covert/operator"
path$(12)	= "/home/user1/tmp3/coven/members"

sqliteconnect #mem, ":memory:"
#mem execute("CREATE TABLE dirTree (seq,pos,dir)")

for i = 1 to 12
j = 1
[loop]
j 	= instr(path$(i),"/",j + 1)
if j > 0 then
	dir$	= mid$(path$(i),1,j)
	mem$	= "INSERT INTO dirTree VALUES (";i;",";j;",'";dir$;"')"
	#mem execute(mem$)
	goto [loop]
end if
next i

mem$ = "SELECT	dir FROM dirTree GROUP BY dir HAVING count(*) = pos ORDER BY pos desc LIMIT 1"
#mem execute(mem$)
rows = #mem ROWCOUNT()		'Get the number of rows
if rows > 0 then
	#row = #mem #nextrow()
	print "====== Largest Directory Common to all Paths ========="
	print #row dir$()
    else
	print "No common Directory"
end if

html "<HR>"

print "========= Common paths ================"

mem$ = "SELECT t.seq as seq,t.pos as pos,t.dir as dir,t1.seq as t1Seq ,t1.dir as t1Dir
FROM	dirTree as t 
JOIN	dirTree as t1
ON	t1.dir = t.dir
AND	t1.seq > t.seq
GROUP BY t.dir,t1.seq"

html "<table border=1><TR align=center>
<TD>Seq</TD>
<TD>Path</TD>
<TD>Common Dir</TD>
<TD>Seq</TD>
<TD>With Path</TD></TR>"

#mem execute(mem$)
WHILE   #mem hasanswer()
	#row 	= #mem #nextrow()
	seq	= #row seq()
	t1Seq	= #row t1Seq()
	pos	= #row pos()
	dir$	= #row dir$()
	t1Dir$	= #row t1Dir$()
html "<TR>"
html "<TD>";seq;"</TD>"
html "<TD>";path$(seq);"</TD>"
html "<TD>";dir$;"</TD>"
html "<TD>";t1Seq;"</TD>"
html "<TD>";path$(t1Seq);"</TD>"
html "</TR>"
WEND
html "</TABLE>"
wait
end

========= Common paths ================
shows only the first few common paths..

SeqPathCommon DirSeqWith Path
1/home/user1/tmp/coverage/test/home/2/home/user1/tmp/covert/operator
2/home/user1/tmp/covert/operator/home/3/home/user1/tmp/coven/members
3/home/user1/tmp/coven/members/home/4/home/user1/tmp1/coverage/test
4/home/user1/tmp1/coverage/test/home/5/home/user1/tmp1/covert/operator
5/home/user1/tmp1/covert/operator/home/6/home/user1/tmp1/coven/members
6/home/user1/tmp1/coven/members/home/7/home/user1/tmp2/coverage/test
7/home/user1/tmp2/coverage/test/home/8/home/user1/tmp2/covert/operator
8/home/user1/tmp2/covert/operator/home/9/home/user1/tmp2/coven/members
9/home/user1/tmp2/coven/members/home/10/home/user1/tmp3/coverage/test
10/home/user1/tmp3/coverage/test/home/11/home/user1/tmp3/covert/operator
11/home/user1/tmp3/covert/operator/home/12/home/user1/tmp3/coven/members
1/home/user1/tmp/coverage/test/home/user1/2/home/user1/tmp/covert/operator
2/home/user1/tmp/covert/operator/home/user1/3/home/user1/tmp/coven/members
3/home/user1/tmp/coven/members/home/user1/4/home/user1/tmp1/coverage/test
4/home/user1/tmp1/coverage/test/home/user1/5/home/user1/tmp1/covert/operator
5/home/user1/tmp1/covert/operator/home/user1/6/home/user1/tmp1/coven/members
6/home/user1/tmp1/coven/members/home/user1/7/home/user1/tmp2/coverage/test
7/home/user1/tmp2/coverage/test/home/user1/8/home/user1/tmp2/covert/operator
8/home/user1/tmp2/covert/operator/home/user1/9/home/user1/tmp2/coven/members
9/home/user1/tmp2/coven/members/home/user1/10/home/user1/tmp3/coverage/test
10/home/user1/tmp3/coverage/test/home/user1/11/home/user1/tmp3/covert/operator
11/home/user1/tmp3/covert/operator/home/user1/12/home/user1/tmp3/coven/members
1/home/user1/tmp/coverage/test/home/user1/tmp/2/home/user1/tmp/covert/operator
2/home/user1/tmp/covert/operator/home/user1/tmp/3/home/user1/tmp/coven/members
4/home/user1/tmp1/coverage/test/home/user1/tmp1/5/home/user1/tmp1/covert/operator
5/home/user1/tmp1/covert/operator/home/user1/tmp1/6/home/user1/tmp1/coven/members
7/home/user1/tmp2/coverage/test/home/user1/tmp2/8/home/user1/tmp2/covert/operator
8/home/user1/tmp2/covert/operator/home/user1/tmp2/9/home/user1/tmp2/coven/members
10/home/user1/tmp3/coverage/test/home/user1/tmp3/11/home/user1/tmp3/covert/operator
11/home/user1/tmp3/covert/operator/home/user1/tmp3/12/home/user1/tmp3/coven/members

Rust

Rust has specific types for owned and borrowed paths. PathBuf is an 'owned' pointer to a path, Path is a borrow; this is similar to String and str, respectively.

use std::path::{Path, PathBuf};

fn main() {
    let paths = [
        Path::new("/home/user1/tmp/coverage/test"),
        Path::new("/home/user1/tmp/covert/operator"),
        Path::new("/home/user1/tmp/coven/members"),
    ];
    match common_path(&paths) {
        Some(p) => println!("The common path is: {:#?}", p),
        None => println!("No common paths found"),
    }
}

fn common_path<I, P>(paths: I) -> Option<PathBuf>
where
    I: IntoIterator<Item = P>,
    P: AsRef<Path>,
{
    let mut iter = paths.into_iter();
    let mut ret = iter.next()?.as_ref().to_path_buf();
    for path in iter {
        if let Some(r) = common(ret, path.as_ref()) {
            ret = r;
        } else {
            return None;
        }
    }
    Some(ret)
}

fn common<A: AsRef<Path>, B: AsRef<Path>>(a: A, b: B) -> Option<PathBuf> {
    let a = a.as_ref().components();
    let b = b.as_ref().components();
    let mut ret = PathBuf::new();
    let mut found = false;
    for (one, two) in a.zip(b) {
        if one == two {
            ret.push(one);
            found = true;
        } else {
            break;
        }
    }
    if found {
        Some(ret)
    } else {
        None
    }
}

Scala

Naive

This simple solution solves the task as given, but has oddities for edge cases due to the implementation of java.lang.String#split.

object FindCommonDirectoryPath extends App {
  def commonPath(paths: List[String]): String = {
    def common(a: List[String], b: List[String]): List[String] = (a, b) match {
      case (a :: as, b :: bs) if a equals b => a :: common(as, bs)
      case _ => Nil
    }
    if (paths.length < 2) paths.headOption.getOrElse("")
    else paths.map(_.split("/").toList).reduceLeft(common).mkString("/")
  }

  val test = List(
    "/home/user1/tmp/coverage/test",
    "/home/user1/tmp/covert/operator",
    "/home/user1/tmp/coven/members"
  )
  println(commonPath(test))
}

Output:

/home/user1/tmp

Advanced

This implementation will handle various edge cases and relative paths. It also includes any common trailing '/' but callers can remove this if desired.

object FindCommonDirectoryPathRelative extends App {
  def commonPath(paths: List[String]): String = {
    val SEP = "/"
    val BOUNDARY_REGEX = s"(?=[$SEP])(?<=[^$SEP])|(?=[^$SEP])(?<=[$SEP])"
    def common(a: List[String], b: List[String]): List[String] = (a, b) match {
      case (a :: as, b :: bs) if a equals b => a :: common(as, bs)
      case _ => Nil
    }
    if (paths.length < 2) paths.headOption.getOrElse("")
    else paths.map(_.split(BOUNDARY_REGEX).toList).reduceLeft(common).mkString
  }

  val test = List(
    "/home/user1/tmp/coverage/test",
    "/home/user1/tmp/covert/operator",
    "/home/user1/tmp/coven/members"
  )
  println(commonPath(test).replaceAll("/$", ""))

  // test cases
  assert(commonPath(test.take(1)) == test.head)
  assert(commonPath(Nil) == "")
  assert(commonPath(List("")) == "")
  assert(commonPath(List("/")) == "/")
  assert(commonPath(List("/", "")) == "")
  assert(commonPath(List("/", "/a")) == "/")
  assert(commonPath(List("/a", "/b")) == "/")
  assert(commonPath(List("/a", "/a")) == "/a")
  assert(commonPath(List("/a/a", "/b")) == "/")
  assert(commonPath(List("/a/a", "/b")) == "/")
  assert(commonPath(List("/a/a", "/a")) == "/a")
  assert(commonPath(List("/a/a", "/a/b")) == "/a/")
  assert(commonPath(List("/a/b", "/a/b")) == "/a/b")
  assert(commonPath(List("a", "/a")) == "")
  assert(commonPath(List("a/a", "/a")) == "")
  assert(commonPath(List("a/a", "/b")) == "")
  assert(commonPath(List("a", "a")) == "a")
  assert(commonPath(List("a/a", "b")) == "")
  assert(commonPath(List("a/a", "b")) == "")
  assert(commonPath(List("a/a", "a")) == "a")
  assert(commonPath(List("a/a", "a/b")) == "a/")
  assert(commonPath(List("a/b", "a/b")) == "a/b")
  assert(commonPath(List("/a/", "/b/")) == "/")
  assert(commonPath(List("/a/", "/a/")) == "/a/")
  assert(commonPath(List("/a/a/", "/b/")) == "/")
  assert(commonPath(List("/a/a/", "/b/")) == "/")
  assert(commonPath(List("/a/a/", "/a/")) == "/a/")
  assert(commonPath(List("/a/a/", "/a/b/")) == "/a/")
  assert(commonPath(List("/a/b/", "/a/b/")) == "/a/b/")
}

Seed7

Seed7 has a standard path representation:

  • The slash ('/') is used as path delimiter.
  • Drive letters are not allowed, but there is a solution to replace them.
  • Except for the path "/" a standard path is not allowed to end with a slash.

Therefore Seed7 programs do not need to consider varying path delimiters, but they need to make sure that a path does not end with a slash.

$ include "seed7_05.s7i";

const func integer: commonLen (in array string: names, in char: sep) is func
  result
    var integer: result is -1;
  local
    var integer: index is 0;
    var integer: pos is 1;
  begin
    if length(names) <> 0 then
      repeat
        for index range 1 to length(names) do
          if pos > length(names[index]) or names[index][pos] <> names[1][pos] then
            decr(pos);
            while pos >= 1 and names[1][pos] <> sep do
              decr(pos);
            end while;
            if pos > 1 then
              decr(pos);
            end if;
            result := pos;
          end if;
        end for;
        incr(pos);
      until result <> -1;
    end if;
  end func;
 
const proc: main is func
  local
    var integer: length is 0;
    const array string: names is [] ("/home/user1/tmp/coverage/test",
                                     "/home/user1/tmp/covert/operator",
                                     "/home/user1/tmp/coven/members")
  begin
    length := commonLen(names, '/');
    if length = 0 then
      writeln("No common path");
    else
      writeln("Common path: " <& names[1][.. length]);
    end if;
  end func;

Output:

Common path: /home/user1/tmp

Sidef

var dirs = %w(
    /home/user1/tmp/coverage/test
    /home/user1/tmp/covert/operator
    /home/user1/tmp/coven/members
);

var unique_pref = dirs.map{.split('/')}.abbrev.min_by{.len};
var common_dir  = [unique_pref, unique_pref.pop][0].join('/');
say common_dir;   # => /home/user1/tmp

Standard ML

fun takeWhileEq ([], _) = []
  | takeWhileEq (_, []) = []
  | takeWhileEq (x :: xs, y :: ys) =
      if x = y then x :: takeWhileEq (xs, ys) else []

fun commonPath sep =
  let
    val commonInit = fn [] => [] | x :: xs => foldl takeWhileEq x xs
    and split = String.fields (fn c => c = sep)
    and join = String.concatWith (str sep)
  in
    join o commonInit o map split
  end

val paths = [
  "/home/user1/tmp/coverage/test",
  "/home/user1/tmp/covert/operator",
  "/home/user1/tmp/coven/members"
]

val () = print (commonPath #"/" paths ^ "\n")

Swift

The below solution works only in swift in Linux.

import Foundation


func getPrefix(_ text:[String]) -> String? {
    var common:String = text[0]
    for i in text {
        common = i.commonPrefix(with: common)
    }
    return common
}

var test = ["/home/user1/tmp/coverage/test", 
 "/home/user1/tmp/covert/operator",
 "/home/user1/tmp/coven/members"]

var output:String = getPrefix(test)!
print(output)

Works on MacOS

import Foundation

func commonPrefix<T: Equatable>(_ lhs: [T], _ rhs: [T]) -> [T] {
	for tryLen in (0...min(lhs.count,rhs.count)).reversed() {
		if lhs.starts(with: rhs.prefix(tryLen)) {
			return Array<T>(rhs.prefix(tryLen))
		}
	}
	return []
}

var test = ["/home/user1/tmp/coverage/test",
		    "/home/user1/tmp/covert/operator",
		    "/home/user1/tmp/coven/members"]

let lcp: String = test.reduce("") { lhs, rhs in
	if !lhs.isEmpty {
		var commonSoFar = commonPrefix(
			lhs.components(separatedBy: "/"),
			rhs.components(separatedBy: "/")
		)
		return commonSoFar.joined(separator: "/")
	}
	return rhs
}
print("Longest common path: \(lcp)")

// Longest common path: /home/user1/tmp

Tcl

package require Tcl 8.5
proc pop {varname} {
    upvar 1 $varname var
    set var [lassign $var head]
    return $head
}

proc common_prefix {dirs {separator "/"}} {
    set parts [split [pop dirs] $separator]
    while {[llength $dirs]} {
        set r {}
        foreach cmp $parts elt [split [pop dirs] $separator] {
            if {$cmp ne $elt} break
            lappend r $cmp
        }
        set parts $r
    }
    return [join $parts $separator]
}
% common_prefix {/home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members}
/home/user1/tmp

TUSCRIPT

$$ MODE TUSCRIPT
common=""
dir1="/home/user1/tmp/coverage/test"
dir2="/home/user1/tmp/covert/operator"
dir3="/home/user1/tmp/coven/members"
dir1=SPLIT (dir1,":/:"),dir2=SPLIT (dir2,":/:"), dir3=SPLIT (dir3,":/:")
LOOP d1=dir1,d2=dir2,d3=dir3
 IF (d1==d2,d3) THEN
  common=APPEND(common,d1,"/")
 ELSE
  PRINT common
  EXIT
 ENDIF
ENDLOOP

Output:

/home/user1/tmp/

UNIX Shell

The following is a pure Bourne Shell solution. The while loop controls the maximum depth to check paths.

#!/bin/sh

pathlist='/home/user1/tmp/coverage/test
/home/user1/tmp/covert/operator
/home/user1/tmp/coven/members'

i=2

while [ $i -lt 100 ]
do
  path=`echo "$pathlist" | cut -f1-$i -d/ | uniq -d`
  if [ -z "$path" ]
  then
     echo $prev_path
     break
  else
     prev_path=$path
  fi
  i=`expr $i + 1`
done

Ursala

The algorithm is to lex the paths into component directory names, and then find the greatest common prefix of those.

#import std

comdir"s" "p" = mat"s" reduce(gcp,0) (map sep "s") "p"

where "s" is a dummy variable representing the separator, "p" is a dummy variable representing the list of paths, and

  • sep is second order function in the standard library that takes a separator character and returns a lexer mapping a string containing the separator to a list of the substrings found between occurrences of it
  • map is the conventional mapping combinator, which takes a function operating on items of a list to a function operating pointwise on a whole list
  • gcp is a polymorphic greatest-common-prefix library function working on pairs of strings or lists of any type
  • reduce is the standard functional programming reduction combinator, which cumulatively applies a binary operator to a list of operands given the operator and the vacuous case result
  • mat is a second order function in the standard library that takes a separator character and returns a function that flattens a list of strings into a single string with copies of the separator inserted between them

Here is a version using operators instead of mnemonics for map and reduce.

comdir"s" "p" = mat"s" gcp:-0 sep"s"* "p"

Here is one in partly point-free form, using the composition operator (+).

comdir"s" = mat"s"+ gcp:-0+ sep"s"*

Here it is in point-free form.

comdir = +^/mat gcp:-0++ *+ sep

test program:

#cast %s

test = 

comdir`/ <
   '/home/user1/tmp/coverage/test',
   '/home/user1/tmp/covert/operator',
   '/home/user1/tmp/coven/members'>

output:

'/home/user1/tmp'

VBScript

Works with: Windows Script Host version *
' Read the list of paths (newline-separated) into an array...
strPaths = Split(WScript.StdIn.ReadAll, vbCrLf)
 
' Split each path by the delimiter (/)...
For i = 0 To UBound(strPaths)
	strPaths(i) = Split(strPaths(i), "/")
Next

With CreateObject("Scripting.FileSystemObject")

	' Test each path segment...
	For j = 0 To UBound(strPaths(0))
		
		' Test each successive path against the first...
		For i = 1 To UBound(strPaths)
			If strPaths(0)(j) <> strPaths(i)(j) Then Exit For
		Next

		' If we didn't make it all the way through, exit the block...
		If i <= UBound(strPaths) Then Exit For
		
		' Make sure this path exists...
		If Not .FolderExists(strPath & strPaths(0)(j) & "/") Then Exit For
		strPath = strPath & strPaths(0)(j) & "/"
		
	Next

End With

' Remove the final "/"...
WScript.Echo Left(strPath, Len(strPath) - 1)

Visual Basic

Works with: Visual Basic version 5
Works with: Visual Basic version 6
Works with: VBA version 6.5
Works with: VBA version 7.1
Public Function CommonDirectoryPath(ParamArray Paths()) As String
Dim v As Variant
Dim Path() As String, s As String
Dim i As Long, j As Long, k As Long
Const PATH_SEPARATOR As String = "/"
  
  For Each v In Paths
    ReDim Preserve Path(0 To i)
    Path(i) = v
    i = i + 1
  Next v
  
  k = 1
  
  Do
    For i = 0 To UBound(Path)
      If i Then
        If InStr(k, Path(i), PATH_SEPARATOR) <> j Then
          Exit Do
        ElseIf Left$(Path(i), j) <> Left$(Path(0), j) Then
          Exit Do
        End If
      Else
        j = InStr(k, Path(i), PATH_SEPARATOR)
        If j = 0 Then
          Exit Do
        End If
      End If
    Next i
    s = Left$(Path(0), j + CLng(k <> 1))
    k = j + 1
  Loop
  CommonDirectoryPath = s
  
End Function

Sub Main()

' testing the above function

Debug.Assert CommonDirectoryPath( _
 "/home/user1/tmp/coverage/test", _
 "/home/user1/tmp/covert/operator", _
 "/home/user1/tmp/coven/members") = _
 "/home/user1/tmp"
 
 Debug.Assert CommonDirectoryPath( _
 "/home/user1/tmp/coverage/test", _
 "/home/user1/tmp/covert/operator", _
 "/home/user1/tmp/coven/members", _
 "/home/user1/abc/coven/members") = _
 "/home/user1"

Debug.Assert CommonDirectoryPath( _
 "/home/user1/tmp/coverage/test", _
 "/hope/user1/tmp/covert/operator", _
 "/home/user1/tmp/coven/members") = _
 "/"

End Sub

Wren

var findCommonDir = Fn.new { |paths, sep|
    var count = paths.count
    if (count == 0) return ""
    if (count == 1) return paths[0]
    var splits = List.filled(count, null)
    for (i in 0...count) splits[i] = paths[i].split(sep)
    var minLen = splits[0].count
    for (i in 1...count) {
        var c = splits[i].count
        if (c < minLen) minLen = c
    }
    if (minLen < 2) return ""
    var common = ""
    for (i in 1...minLen) {
        var dir = splits[0][i]
        for (j in 1...count) {
            if (splits[j][i] != dir) return common
        }
        common = common + sep + dir
    }
    return common
}

var paths = [
    "/home/user1/tmp/coverage/test",
    "/home/user1/tmp/covert/operator",
    "/home/user1/tmp/coven/members"
]
System.write("The common directory path is: ")
System.print(findCommonDir.call(paths, "/"))
Output:
The common directory path is: /home/user1/tmp

Yabasic

Translation of: GW-BASIC
x$ = "/home/user1/tmp/coverage/test"
y$ = "/home/user1/tmp/covert/operator"
z$ = "/home/user1/tmp/coven/members"

a = len(x$)
if a > len(y$)  a = len(y$)
if a > len(z$)  a = len(z$)
for i = 1 to a
    if mid$(x$, i, 1) <> mid$(y$, i, 1)  break
next i
a = i - 1

for i = 1 to a
    if mid$(x$, i, 1) <> mid$(z$, i, 1)  break
next i
a = i - 1

if mid$(x$, i, 1) <> "/" then
    for i = a to 1 step -1
        if "/" = mid$(x$, i, 1)  break
    next i
fi

REM Task description says no trailing slash, so...
a = i - 1
print "Common path is '", left$(x$, a), "'"

zkl

dirs:=T("/home/user1/tmp/coverage/test", "/home/user1/tmp/covert/operator",
        "/home/user1/tmp/coven/members");
n:=Utils.zipWith('==,dirs.xplode()).find(False); // character pos which differs
n=dirs[0][0,n].rfind("/");  // find last "/"
dirs[0][0,n];
Output:
/home/user1/tmp

Will throw an error if no match, "" if common dir is "/"