What is the best way extract the common file path from the list of file path strings in c#?
Eg: I have a list 5 file paths in List variable, like below
c:\
The top answer fails for identical paths like e.g.:
string str1 = @"c:\dir\dir1\dir2\dir3";
string str2 = @"c:\dir\dir1\dir2\dir3";
This is better: Find common prefix of strings
however
.TakeWhile(s => s.All(d => d == s.First()))
should be
.TakeWhile(s =>
{
var reference = s.First();
return s.All(d => string.Equals(reference, d, StringComparison.OrdinalIgnoreCase));
})
You could break the paths down into segments (i.e. split by the backslash), then build back a segment at a time and compare the results until you find the end of the match. I doubt that is the best way, but it would work.
Use the first path as the iterator seed:
using System;
using System.Collections.Generic;
using System.IO;
namespace stackoverflow1
{
class MainClass
{
public static void Main (string[] args)
{
List<String> paths=new List<String>();
paths.Add(@"c:\abc\pqr\tmp\sample\b.txt");
paths.Add(@"c:\abc\pqr\tmp\new2\c1.txt");
paths.Add(@"c:\abc\pqr\tmp\b2.txt");
paths.Add(@"c:\abc\pqr\tmp\b3.txt");
paths.Add(@"c:\abc\pqr\tmp\tmp2\b2.txt");
Console.WriteLine("Found: "+ShortestCommonPath(paths));
}
private static String ShortestCommonPath(IList<String> list)
{
switch (list.Count)
{
case 0: return null;
case 1: return list[0];
default:
String s=list[0];
while (s.Length>0)
{
bool ok=true;
for (int i=1;i<list.Count;i++)
{
if (!list[i].StartsWith(s))
{
ok=false;
int p=s.LastIndexOf(Path.DirectorySeparatorChar);
if (p<0) return "";
s=s.Substring(0,p);
break;
}
}
if (ok) break;
}
return s;
}
}
}
}
I don't think there's a "trick"to get past using the brute force method to do this comparison, but you can optimize your solution somewhat:
Prepare:
Find the shortest string
Repeat:
See if all of the other strings contain it
If so, you're done
If not, remove one or more characters
I would use a loop and on each string I'd split it with s.Split('\')
.
Then it iterate over the first element, and next element, saving them as I go.
When I find one that is different, I can return the last iteration's result.
string commonPath(string[] paths) {
// this is a Java notation, I hope it's right in C# as well? Let me know!
string[][] tokens = new string[paths.length][];
for(int i = 0; i < paths.Length; i++) {
tokens[i] = paths.Split('\\');
}
string path = "";
for(int i = 0; i < tokens[0].Length; i++) {
string current = tokens[0][i];
for(int j = 1; j < tokens.Length; j++) {
if(j >= tokens[i].Length) return path;
if(current != tokens[i][j]) return path;
}
path = path + current + '\\';
}
return path; // shouldn't reach here, but possible on corner cases
}
Here is a quick implementation that just loops through the list of strings and then compares the characters at the beginning of the strings trimming from the original string:
List<string> list1 = new List<string>();
list1.Add(@"c:\abc\pqr\tmp\sample\b.txt");
list1.Add(@"c:\abc\pqr\tmp\new2\c1.txt");
list1.Add(@"c:\abc\pqr\tmp\b2.txt");
list1.Add(@"c:\abc\pqr\tmp\b3.txt");
list1.Add(@"c:\abc\pqr\tmp\tmp2\b2.txt");
string baseDir = "";
foreach (var item in list1)
{
if (baseDir == "")
baseDir = System.IO.Path.GetDirectoryName(item);
else
{
int index = 0;
string nextDir = System.IO.Path.GetDirectoryName(item);
while (index< baseDir.Length && index<nextDir.Length &&
baseDir[index] == nextDir[index])
{
index++;
}
baseDir = baseDir.Substring(0, index);
}
}
MessageBox.Show(baseDir);