C# get directories ordered by name starting from X - c#

I need to get the directories within another directory but only those that are "after" X ordering them by dir name.
Example:
-Root
|--DirectoryA
|--DirectoryB
|--DirectoryC
|--DirectoryD
So basically something like this:
foreach(string path in Directory.GetDirectories("/root/"))
{
Console.WriteLine(path);
}
Output:
/root/DirectoryA
/root/DirectoryB
/root/DirectoryC
/root/DirectoryD
But I actually need to get only those after C (including it), I guess with Linq it shouldn't be too hard, but I really can't see how.
Maybe something like:
Directory.GetDirectories("/root/").OrderByDescending(dirName =>
Path.GetDirectoryName(dirName)) ??

You can use SkipWhile, assuming the order is alphabetical:
foreach(string path in Directory.GetDirectories("/root/")
.Select(dir => Path.GetFileName(dir))
.SkipWhile(dir => string.Compare(dir, "DirectoryC") < 0)
{
Console.WriteLine(path);
}
This way you will avoid redundant filtering will after encountering the first valid directory.
If you would want to take only the folders before DirectoryC you could use TakeWhile:
foreach(string path in Directory.GetDirectories("/root/")
.Select(dir => Path.GetFileName(dir))
.TakeWhile(dir => string.Compare(dir, "DirectoryC") < 0)
{
Console.WriteLine(path);
}
Note that string.Compare is culture dependent and you may use other overloads to customize its behavior.

Use the Where extension method
foreach(string path in Directory
.GetDirectories("/root/")
.Where(d => String.Compare(Path.GetFileName(d), "DirectoryC") > 0))
{
Console.WriteLine(path);
}
You can also include a ordering if you need it, but it will not influence the filtering with Where. If you do, append it after Where for efficiency.

or just in case that you are using an older version of .NET you can always write the logic yourself:
var directories = Directory.GetDirectories("C:\\").OrderByDescending(r => r).ToArray();
if (directories != null && directories.Length > 0)
{
for (int i = 0, Cindex=-1; i < directories.Length; i++)
{
if (Cindex > 0)
{
Console.WriteLine(directories[i]);
continue;
}
if (directories[i] == "DirectoryC")
{
Cindex = i;
}
}
}

Related

How to check if a string is in an array

I am trying to check whether a string is in an array and if continues even though the fileInfo.Name.Contains a string that is in files.Any:
\\FILES LIKE DATABASE.MDB IS IN C:PROJECTS\HOLON\DATABASE.MDB
**if (files.Any((fileInfo.Name.Contains)))**
\\DO SOMETHING
Console.WriteLine(
fileInfo.Name, fileInfo.Length,
If you alread have the filenames collected in an array, then you should either do it this way:
if (files.Any() && files.Contains(fileInfo.Name))
{
// Do something
}
If you just want to check if a file exists then you can use File.Exists:
if(System.IO.File.Exists(fileInfo.Name))
{
// Do Something
}
So you have a collection of full file paths? And you want to check if one or more of those list entries match with a specific file name?
Perhaps this would work for you:
string fileToSearch = "DATABASE.MDB";
bool found = files.Any(fileName => new FileInfo(fileName).Name.ToUpper() == fileToSearch.ToUpper());
Edit:
An alternative to constructing new FileInfo objects would be to use System.IO.Path:
bool found = files.Any(fileName => Path.GetFileName(fileName).ToUpper() == fileToSearch.ToUpper());
Edit 2:
On the other hand, if you want to search for a specific file name, and you want to use the result, you could do something like this:
var fileToSearch = "DATABASE.MDB";
var fileInfo =
(from f in files
let fi = new FileInfo(f)
where fi.Name.ToUpper() == fileToSearch.ToUpper()
select fi).FirstOrDefault();
if (fileInfo != null)
{
if (fileInfo.Exists)
{
Console.WriteLine($"{fileInfo.Name} ({fileInfo.Length} bytes).");
}
else
{
Console.WriteLine($"{fileInfo.Name} (does not exist).");
}
}
I used a LINQ query here for readability. You could use the extension methods (files.Select(f => new FileInfo(f)).Where(fi => fi.Name.ToUpper() == fileToSearch.ToUpper()).FirstOrDefault()) as well, but that's up to you.
if (Array.Exists(files, element => element.Contains(fileInfo.Name)))

How to get a latest or max folder version?

I have some version folders like Vx_x_x
I want to retrieve the max folder version.
For example:
Folder contains,
V8_2_1
V9_3_2
V10_4_1
I want to check the max number next to V and so on to get a latest folder version.
I am able to get a list of folders, But confusion to get how can I get a max number. If anyone can suggest me would me a great helpful. Thank you.
private static void GetFolderVersion()
{
string startFolder = #"C:\Version\";
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(startFolder);
IEnumerable<System.IO.DirectoryInfo> directoryList = dir.GetDirectories("*.*", System.IO.SearchOption.AllDirectories);
}
I would consider using the built-in System.Version type. Assuming all directory names are in the same format of "VX_Y_Z" (where X, Y and Z represent one or more digits, and V represents a literal "V"), the following code will do what you want:
public string GetMaxVersion(IEnumerable<string> directoryNames)
{
var vDict = directoryNames.ToDictionary(
s => new Version(s.Substring(1).Replace("_", ".")),
s => s);
var maxKey = vDict.Keys.Max();
return vDict[maxKey];
}
Here we build a dictionary of version to file name mappings (note that we change the string format from "VX_Y_Z" to "X.Y.Z" to be able to create a System.Version object). All that remains is to retrieve the max value of all dictionary keys, and return the value assigned to that given key, which will be the directory name you're looking for.
UPDATE: For completeness, here's a piece of code that uses the method above and takes care of everything:
public string GetMaxVersionDirectory(string rootDirectory)
{
var dirNames = Directory.GetDirectories(rootDirectory, "V*_*_*")
.Select(dir => Path.GetFileName(dir));
return GetMaxVersion(dirNames);
}
In your case, you need to pass #"C:\Version" as the rootDirectory parameter.
If the files all match the pattern you gave, I'd be tempted to extract the version information using a regular expression, then selecting the highest value, starting with the major version and working out.
Update: replace the V in the regular expression with the correct prefix for your case.
var regex = new Regex(#"^V(\d+)_(\d+)_(\d+)$", RegexOptions.Compiled);
var versions = directoryList
.Select(f => regex.Match(f))
.Where(m => m.Success)
.Select(m => new
{
Major = Int32.Parse(m.Groups[1].Value),
Minor = Int32.Parse(m.Groups[2].Value),
Patch = Int32.Parse(m.Groups[3].Value)
}).ToList();
var major = versions.Max(a => a.Major);
versions = versions
.Where(a => a.Major == major)
.ToList();
var minor = versions.Max(a => a.Minor);
versions = versions
.Where(a => a.Minor == minor)
.ToList();
var patch = versions.Max(a => a.Patch);
versions = versions
.Where(a => a.Patch == patch)
.ToList();
var newest = versions.First();
var filename = String.Format("V_{0}_{1}_{2}", newest.Major, newest.Minor, newest.Patch);
Although this probably does work, it's advisable to use the solution by #jbartuszek that uses the Version class.
Something along these lines could do it, but be aware that this is from the top of my head, written in notepad so might not build or contain some form of logical error. This just demonstrates the gist of it:
string[] version = folderName.Split('_');
string[] otherVersion = otherFolderName.Split('_');
then you write a method that checks the individual parts of the version:
private int CompareVersions(string[] version, string[] otherVersion)
{
//we won't need the first character (the 'V')
int vmajor = int.Parse(version[0].Substring(1));
int vminor = int.Parse(version[1]);
int vrevision = int.Parse(version[2]);
int ovmajor = int.Parse(otherVersion[0].Substring(1));
int ovminor = int.Parse(otherVersion[1]);
int ovrevision = int.Parse(otherVersion[2]);
int majorCompare = vmajor.CompareTo(ovmajor);
//check if major already decides outcome
if(majorCompare != 0)
{
return majorCompare;
}
int minorCompare = vminor.CompareTo(ovminor);
//then if major equal, check if minor decides outcome
if(minorCompare != 0)
{
return minorCompare;
}
//lastly, return outcome of revision compare
return vrevision.CompareTo(ovrevision);
}
this is how you'd compare two folder names. If you wanted to get the max folder version, you could foreach the folder names:
//we'll start out by assigning the first folder name as a preliminary max
string maxFolder = folderNames[1];
string[] maxFolderVersion = maxFolder.Split('_');
foreach(string folderName in folderNames)
{
if(CompareVersions(folderName.Split('_'), maxFolderVersion) > 0)
{
maxFolder = folderName;
}
}
You can use a mathematical approach. Let's say every single part of your version can go up to max. 1000. So the base is 1000. The version parts are your coefficents and the exponents are built in your loop. By summing up coefficent*base^exp you get a value which you can compare to get the highest version:
private static string GetHighestFolderVersion()
{
string startFolder = #"C:\Version\";
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(startFolder);
IEnumerable<System.IO.DirectoryInfo> directoryList = dir.GetDirectories("*.*", System.IO.SearchOption.AllDirectories);
KeyValuePair<string, long> highestVersion = new KeyValuePair<string, long>("", 0);
foreach (System.IO.DirectoryInfo dirInfo in directoryList)
{
string versionOrigName = dirInfo.Name;
string versionStr = versionOrigName.Substring(1);
List<string> versionParts = versionStr.Split('_').ToList<string>();
long versionVal = 0;
int exp = 0;
for (int i = versionParts.Count - 1; i > -1; i--)
{
versionVal += (long.Parse(versionParts[i]) * (long)(Math.Pow(1000, exp)));
exp++;
}
if (versionVal > highestVersion.Value)
{
highestVersion = new KeyValuePair<string, long>(versionOrigName, versionVal);
}
}
return highestVersion.Key;
}

Select uploaded files by key

In an ASP.NET application I want to process uploaded files. The HttpContext.Current.Request.Files.AllKeys contains the following:
[0]File2
[1]File2
[2]flTeklif
[3]flTeklif
[4]flTeklif
[5]flTeklif
How can I select only the uploaded files that have key flTeklif into a List<HttpPostedFile>?
I tried this:
var uploads = HttpContext.Current.Request.Files.AllKeys
.Where(s=>s.stringname == "flTeklif")
But that only selects the keys, not the files. How can I select the Files.Where(key == "flTeklif")?
HttpRequest.Files is an HttpFileCollection, whose AllKeys property is a string array.
So you can just use AllKeys.Where(s => s == "flTeklif").
So far for the literal interpretation of your question, which is probably why you're pretty heavily down- and closevoted as it doesn't really make any sense.
If your actual question is "How can I select the files that have flTeklif as their key", use:
var files = HttpContext.Current.Request.Files;
var result = new List<HttpPostedFile>();
for (int i = 0; i < files.AllKeys.Count; i++)
{
if (files.AllKeys[i] == "flTeklif")
{
result.Add(files.AllKeys[i]);
}
}
Then result will contain the files you're interested in.
OK i understand. may be
HttpContext.Current.Request.Files.Cast<HttpPostedFile>().Where(c => c.FileName.Contains("flTeklif")).ToList();
Use HttpFileCollection.GetKey(Int32) method. There is no mention that AllKeys array order is the similiar to the files HttpFileCollection.
int i;
HttpFileCollection MyFileColl = Request.Files;
for( i= 0; i< MyFileColl.Count; i++)
{
if( MyFileColl.GetKey(i) == "flTeklif")
{
//...
}
}
You can use the following code to get a list of all uploaded files against a particular key
HttpFileCollection files = null;
try { files = HttpContext.Current.Request.Files; } catch { }
var allUploadedFilesAgainstThisKey = files.GetMultiple(singleKey); // singleKey is a string
// allUploadedFilesAgainstThisKey is of type IList<HttpPostedFile>

C# - Get a list of files excluding those that are hidden

Directory.GetFiles() returns all files, even those that are marked as hidden. Is there a way to get a list of files that excludes hidden files?
This should work for you:
DirectoryInfo directory = new DirectoryInfo(#"C:\temp");
FileInfo[] files = directory.GetFiles();
var filtered = files.Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden));
foreach (var f in filtered)
{
Debug.WriteLine(f);
}
// check whether a file is hidden
bool isHidden = ((File.GetAttributes(filePath) & FileAttributes.Hidden) == FileAttributes.Hidden);
Using .NET 4.0 and Directory.EnumerateDirectories, you could use this construct :
var hiddenFilesQuery = from file in Directory.EnumerateDirectories(#"c:\temp")
let info = new FileInfo(file)
where (info.Attributes & FileAttributes.Hidden) == 0
select file;
This is basically the same as the other answer, except Directory.EnumerateDirectories is a bit more lazy. This is not very useful if you enumerate everything, though.
(The let is here to have the query a but more readeable).
if use use:
var filtered = files.Select(f => f)
.Where(f => (f.Attributes & FileAttributes.Hidden) == 0);
this only find no hidden file, so you can use :
var filtered = files.Select(f => f)
.Where(f => (f.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden);
this is only to read the hidden file
One line Code:
FileInfo[] tmpFiles = tempDir.GetFiles().Where(file =>
(file.Attributes & FileAttributes.Hidden) == 0).ToArray();
I actually rather like passing a function parameter to a method which does what I want it to. I have a SearchDirectory method, which forms the basis for most of the calls I use:
private void SearchDirectory(DirectoryInfo startDirectory,
string pattern,
Action<FileInfo> act)
{
foreach (var file in startDirectory.GetFiles(pattern))
act(file);
foreach (var directory in startDirectory.GetDirectories())
SearchDirectory(directory, pattern, act);
}
private List<FileInfo> SearchDirectory(DirectoryInfo startDirectory,
string pattern,
Func<FileInfo, bool> isWanted)
{
var lst = new List<FileInfo>();
SearchDirectory(startDirectory,
pattern,
(fi) => { if (isWanted(fi)) lst.Add(fi); });
return lst;
}
Then you can use the other solutions listed to write an IsHidden function which takes a single FileInfo, and returns true if so:
private bool IsHiddenDirectory(DirectoryInfo d) {
if (d == null) return false;
if (d.Attributes.HasFlag(FileAttributes.Hidden))) return true;
if (d.Parent == null) return false;
return IsHiddenDirectory(d.Parent);
}
private bool IsHidden(FileInfo fi) {
if ((fi.Attributes & FileAttributes.Hidden) != 0) return true;
// If you're worried about parent directories hidden:
return IsHiddenDirectory(fi.Directory);
// otherwise:
return false;
}
Then I can call it in another method pretty easily:
var files = SearchDirectory(new DirectoryInfo("C:\temp\"),
"*.xml",
(fi) => { return !IsHidden(fi); );
If you're using SearchOption.TopDirectoryOnly - then it's relatively simple, however - it gets much more complex if you want to list all files recursively using SearchOption.AllDirectories. If can you GetFiles and then filter out read only, but unfortunately it will not work with directories marked as hidden. Files under that folders gets listed as well, but they are not hidden unlike directory.
You can use also GetDirectories, but again - you cannot list everything recursively using SearchOption.AllDirectories, since it also lists folders which resides under hidden folder, but those folders do not have hidden attribute enabled.
This is the case at least for Tortoise svn .svn hidden folder. It contains a lot of folders which are not hidden, but .svn is hidden. Finally I've wrote function which looks like this:
SearchOption sopt = SearchOption.AllDirectories;
List<String> listFiles = new List<string>();
List<DirectoryInfo> dirs2scan = new List<DirectoryInfo>();
dirs2scan.Add(new DirectoryInfo(fromPath) );
for( ; dirs2scan.Count != 0; )
{
int scanIndex = dirs2scan.Count - 1; // Try to preserve somehow alphabetic order which GetFiles returns
// by scanning though last directory.
FileInfo[] filesInfo = dirs2scan[scanIndex].GetFiles(pattern, SearchOption.TopDirectoryOnly);
foreach (FileInfo fi in filesInfo)
{
if (bNoHidden && fi.Attributes.HasFlag(FileAttributes.Hidden))
continue;
listFiles.Add(fi.FullName);
}
if( sopt != SearchOption.AllDirectories )
break;
foreach (DirectoryInfo dir in dirs2scan[scanIndex].GetDirectories("*", SearchOption.TopDirectoryOnly))
{
if (bNoHidden && dir.Attributes.HasFlag(FileAttributes.Hidden))
continue;
dirs2scan.Add(dir);
}
dirs2scan.RemoveAt(scanIndex);
}
sopt can be used a parameter in function if necessary or removed if not needed.
static bool IsHidden(string p)
{
return p.Contains("Hidden");
}
DirectoryInfo directory = new DirectoryInfo(#"C:\temp");
FileInfo[] files = directory.GetFiles();
var filtered = files.Where(f => !IsHidden(File.GetAttributes(f).ToString()));
foreach (var f in filtered)
{
Debug.WriteLine(f);
}
Steps:
Create bool that returns true when string contains 'Hidden' ---- static bool IsHidden(string p){return p.Contains("Hidden");}
get directory info ---- DirectoryInfo directory = new DirectoryInfo(#"C:\temp");
get file info array from directory ---- FileInfo[] files = directory.GetFiles();
get file info Attributes and convert into string from file info array and check it contains 'Hidden' or not ----
var filtered = files.Where(f=>!IsHidden(File.GetAttributes(f).ToString()));

Compare adjacent list items

I'm writing a duplicate file detector. To determine if two files are duplicates I calculate a CRC32 checksum. Since this can be an expensive operation, I only want to calculate checksums for files that have another file with matching size. I have sorted my list of files by size, and am looping through to compare each element to the ones above and below it. Unfortunately, there is an issue at the beginning and end since there will be no previous or next file, respectively. I can fix this using if statements, but it feels clunky. Here is my code:
public void GetCRCs(List<DupInfo> dupInfos)
{
var crc = new Crc32();
for (int i = 0; i < dupInfos.Count(); i++)
{
if (dupInfos[i].Size == dupInfos[i - 1].Size || dupInfos[i].Size == dupInfos[i + 1].Size)
{
dupInfos[i].CheckSum = crc.ComputeChecksum(File.ReadAllBytes(dupInfos[i].FullName));
}
}
}
My question is:
How can I compare each entry to its neighbors without the out of bounds error?
Should I be using a loop for this, or is there a better LINQ or other function?
Note: I did not include the rest of my code to avoid clutter. If you want to see it, I can include it.
Compute the Crcs first:
// It is assumed that DupInfo.CheckSum is nullable
public void GetCRCs(List<DupInfo> dupInfos)
{
dupInfos[0].CheckSum = null ;
for (int i = 1; i < dupInfos.Count(); i++)
{
dupInfos[i].CheckSum = null ;
if (dupInfos[i].Size == dupInfos[i - 1].Size)
{
if (dupInfos[i-1].Checksum==null) dupInfos[i-1].CheckSum = crc.ComputeChecksum(File.ReadAllBytes(dupInfos[i-1].FullName));
dupInfos[i].CheckSum = crc.ComputeChecksum(File.ReadAllBytes(dupInfos[i].FullName));
}
}
}
After having sorted your files by size and crc, identify duplicates:
public void GetDuplicates(List<DupInfo> dupInfos)
{
for (int i = dupInfos.Count();i>0 i++)
{ // loop is inverted to allow list items deletion
if (dupInfos[i].Size == dupInfos[i - 1].Size &&
dupInfos[i].CheckSum != null &&
dupInfos[i].CheckSum == dupInfos[i - 1].Checksum)
{ // i is duplicated with i-1
... // your code here
... // eventually, dupInfos.RemoveAt(i) ;
}
}
}
I have sorted my list of files by size, and am looping through to
compare each element to the ones above and below it.
The next logical step is to actually group your files by size. Comparing consecutive files will not always be sufficient if you have more than two files of the same size. Instead, you will need to compare every file to every other same-sized file.
I suggest taking this approach
Use LINQ's .GroupBy to create a collection of files sizes. Then .Where to only keep the groups with more than one file.
Within those groups, calculate the CRC32 checksum and add it to a collection of known checksums. Compare with previously calculated checksums. If you need to know which files specifically are duplicates you could use a dictionary keyed by this checksum (you can achieve this with another GroupBy. Otherwise a simple list will suffice to detect any duplicates.
The code might look something like this:
var filesSetsWithPossibleDupes = files.GroupBy(f => f.Length)
.Where(group => group.Count() > 1);
foreach (var grp in filesSetsWithPossibleDupes)
{
var checksums = new List<CRC32CheckSum>(); //or whatever type
foreach (var file in grp)
{
var currentCheckSum = crc.ComputeChecksum(file);
if (checksums.Contains(currentCheckSum))
{
//Found a duplicate
}
else
{
checksums.Add(currentCheckSum);
}
}
}
Or if you need the specific objects that could be duplicates, the inner foreach loop might look like
var filesSetsWithPossibleDupes = files.GroupBy(f => f.FileSize)
.Where(grp => grp.Count() > 1);
var masterDuplicateDict = new Dictionary<DupStats, IEnumerable<DupInfo>>();
//A dictionary keyed by the basic duplicate stats
//, and whose value is a collection of the possible duplicates
foreach (var grp in filesSetsWithPossibleDupes)
{
var likelyDuplicates = grp.GroupBy(dup => dup.Checksum)
.Where(g => g.Count() > 1);
//Same GroupBy logic, but applied to the checksum (instead of file size)
foreach(var dupGrp in likelyDuplicates)
{
//Create the key for the dictionary (your code is likely different)
var sample = dupGrp.First();
var key = new DupStats() {FileSize = sample.FileSize, Checksum = sample.Checksum};
masterDuplicateDict.Add(key, dupGrp);
}
}
A demo of this idea.
I think the for loop should be : for (int i = 1; i < dupInfos.Count()-1; i++)
var grps= dupInfos.GroupBy(d=>d.Size);
grps.Where(g=>g.Count>1).ToList().ForEach(g=>
{
...
});
Can you do a union between your two lists? If you have a list of filenames and do a union it should result in only a list of the overlapping files. I can write out an example if you want but this link should give you the general idea.
https://stackoverflow.com/a/13505715/1856992
Edit: Sorry for some reason I thought you were comparing file name not size.
So here is an actual answer for you.
using System;
using System.Collections.Generic;
using System.Linq;
public class ObjectWithSize
{
public int Size {get; set;}
public ObjectWithSize(int size)
{
Size = size;
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("start");
var list = new List<ObjectWithSize>();
list.Add(new ObjectWithSize(12));
list.Add(new ObjectWithSize(13));
list.Add(new ObjectWithSize(14));
list.Add(new ObjectWithSize(14));
list.Add(new ObjectWithSize(18));
list.Add(new ObjectWithSize(15));
list.Add(new ObjectWithSize(15));
var duplicates = list.GroupBy(x=>x.Size)
.Where(g=>g.Count()>1);
foreach (var dup in duplicates)
foreach (var objWithSize in dup)
Console.WriteLine(objWithSize.Size);
}
}
This will print out
14
14
15
15
Here is a netFiddle for that.
https://dotnetfiddle.net/0ub6Bs
Final note. I actually think your answer looks better and will run faster. This was just an implementation in Linq.

Categories

Resources