Merge 2 foreach loops (Deleting files with specific extensions) - c#

I got the following code :
foreach (string file in Directory.GetFiles(generationDir, "*.xaml")
{
File.Delete(file);
}
foreach (string file in Directory.GetFiles(generationDir, "*.cs")
{
File.Delete(file);
}
I'd like to merge these 2 foreach in a single loop ? Is that possible ?
I'm not comfortable with lambda expression so got a hard time here...

foreach (var file in
Directory.GetFiles(generationDir, "*.*")
.Where(item => item.EndsWith(".xaml") || item.EndsWith(".cs")))
{
File.Delete(file);
}
For those who like ForEach() "sequence operator fake" :)
(Please be aware of “foreach” vs “ForEach” before usign it)
Directory.GetFiles(generationDir, "*.*")
.Where(item => item.EndsWith(".xaml") || item.EndsWith(".cs"))
.ToList()
.ForEach(item => File.Delete(item))
If you are usign .NET Framework v4 you can use more efficient Directory.EnumerateFiles() method:
The EnumerateFiles and GetFiles methods differ as follows: When you
use EnumerateFiles, you can start enumerating the collection of names
before the whole collection is returned; when you use GetFiles, you
must wait for the whole array of names to be returned before you can
access the array. Therefore, when you are working with many files and
directories, EnumerateFiles can be more efficient.
The returned collection is not cached; each call to the GetEnumerator
on the collection will start a new enumeration.

You can just union the two sets
var items = Directory.GetFiles(generationDir, "*.xaml").Where(item => item.EndsWith(".xaml"))
.Union(Directory.GetFiles(generationDir, "*.cs").Where(item => item.EndswWith(".cs"))
Then foreach through them.
By the way, the where clause on each of the collections seems to be redundant to me, since GetFiles uses the mask you provided, so all of the files should end in .xaml or .cs.

How about with LINQ:
var files = from extension in new[] { "xaml", "cs" }
from fileName in Directory.GetFiles(generationDir, "*." + extension)
select fileName;
foreach(var file in files)
File.Delete(file);
Or in method syntax:
var files = new[] { "xaml", "cs" }
.SelectMany(extension => Directory.GetFiles(generationDir, "*." + extension))
foreach(var file in files)
File.Delete(file);

Remove the filter argument from the GetFiles method call and use and or in your where clause:
var files = Directory.GetFiles(generationDir).Where(item => item.EndsWith(".cs") || item.EndsWith(".xaml"));
foreach (var file in files)
{
File.Delete(file);
}

var filenames = (new[] { "*.cs", "*.xaml" }).SelectMany(
ext => Directory.GetFiles(generationDir, ext));
foreach(var filename in filenames)
File.Delete(filename);
I also removed the redundant Where statements since you were double filtering on the same criteria as you passed in to Directory.GetFiles.

try like this using this operator ||
foreach (string file in Directory.GetFiles(generationDir, "*.*")
.Where(item => item.EndsWith(".xaml") || item.EndsWith(".cs"))
{
File.Delete(file);
}

Related

DirectoryInfo.GetFiles with multiple filters

I am trying to get a list of FileInfo objects that satisfy multiple filters.
Every suggestion I have seen uses array of file names/paths instead of FileInfo:
var files = Directory.GetFiles(sLogPath, "*.*", SearchOption.TopDirectoryOnly)
.Where(s => s.StartsWith("abc", StringComparison.CurrentCultureIgnoreCase) || s.StartsWith("def", StringComparison.CurrentCultureIgnoreCase));
What I am trying to get is:
DirectoryInfo di = new DirectoryInfo(sLogPath);
var files = di.GetFiles(<same filter as above>);
But it looks like I can only do something like:
var files = di.GetFiles("*_" + dateStr + ".log");
Based on your comment to me on your question, it looks like you want to filter on file names, but get the FileInfos that correspond to these names.
You can do something like this
var di = new DirectoryInfo(sLogPath);
var files = di
.GetFiles("*.*", SearchOption.TopDirectoryOnly)
.Where(x => x.Name.StartsWith("abc", StringComparison.CurrentCultureIgnoreCase)
|| x.Name.StartsWith("def", StringComparison.CurrentCultureIgnoreCase))
.ToList();
We're using the Name property in the filter and working with the FileInfo[] array returned by DirectoryInfo.GetFiles().

C# How do I Access this generic list, when i didn't create the type?

I've stepped through the first part and that works correctly. My list ends up As fileName[288], and in my locals window I have a "value". This is a list. I didn't create the type, so I don't know how to access it. I know it is a generic list of strings, so I imported System.Collections.Generic.List, but I cant seem to figure it out.
var fileName = new DirectoryInfo(text)
.GetFiles(".", SearchOption.AllDirectories)
.Select(x => x.Name)
.ToList();
for (var i = 0; i < fileName.Count; i++)
{
Console.WriteLine("Filename: {0}", fileName[i].?)
}
Since the last Select returns string (x.Name is of type string)
...
.Select(x => x.Name) // select string(s)
.ToList(); // materialize them into a list
then fileName is of type List<string> and you have no need in any additional method:
Console.WriteLine("Filename: {0}", fileName[i]);
I suggest getting rid of for loop and let (with a help of foreach) .Net work for you:
// we have (potentially) many files' names - let use "fileNames" - plural
var fileNames = new DirectoryInfo(text)
.GetFiles(".", SearchOption.AllDirectories)
.Select(x => x.Name);
foreach (var name in fileNames)
Console.WriteLine($"Filename: {name}"); // string interpolation for readability
Edit: Please, notice that we don't need .ToList() in case of foreach - all we want is to enumerate the names without saving them into an any collection (say List<string>).

How to EnumerateFiles with all subdirectories with C# DirectoryInfo?

I found this code that gets an array of files out of a DirectoryInfo :
FileInfo[] fileInfoArray = di.EnumerateFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
But it only searches the direct children of the path of DirectoryInfo. i.e., it does not include grandchildren.
I guess I need to add SearchOption.AllDirectories parameter to somewhere, but where?
I tried :
di.EnumerateFiles(SearchOption.AllDirectories).Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
But it yields an error.
So how do I search with a pattern, including all subdirectories ?
Thanks for any help !
Look at the overloads of DirectoryInfo.EnumerateFiles - there's no overload taking just a SearchOption, but you can give a string and a SearchOption:
var files = di.EnumerateFiles("*", SearchOption.AllDirectories)
.Where(f => extensions.Contains(f.Extension.ToLower()))
.ToArray();

How to exclude folders when using Directory.GetDirectories

I want to return a list of all the subdirectories in the 'SomeFolder' directory excluding the 'Admin' and 'Templates' directories.
I have the following folder structure (simplified):
C:\inetpub\wwwroot\MyWebsite\SomeFolder\RandomString
C:\inetpub\wwwroot\MyWebsite\SomeFolder\RandomString
C:\inetpub\wwwroot\MyWebsite\SomeFolder\RandomString
C:\inetpub\wwwroot\MyWebsite\SomeFolder\Admin
C:\inetpub\wwwroot\MyWebsite\SomeFolder\Templates
'SomeFolder' can contain a varying number a 'RandomString' folders (anywhere from ~10 to ~100).
Here is what I have tried:
var dirs = Directory.GetDirectories(Server.MapPath(".."))
.Where(s => !s.EndsWith("Admin") || !s.EndsWith("Templates"));
foreach (string dir in dirs)
{
lit.Text += Environment.NewLine + dir;
}
This returns the full list of folders (shown above) without 'Admin' and 'Templates' filtered out.
Interestingly, if I change the LINQ .Where clause to include, instead of exclude, 'Admin' and 'Templates' it works, meaning it returns just the paths for 'Admin' and 'Templates'.
.Where(s => s.EndsWith("Admin") || s.EndsWith("Templates"));
If LINQ is not the solution, is there any way to use the GetDirectories SearchPattern to filter out directories?
You can do something like:
//list your excluded dirs
private List<string> _excludedDirectories= new List<string>() { "Admin", "Templates" };
//method to check
static bool isExcluded(List<string> exludedDirList, string target)
{
return exludedDirList.Any(d => new DirectoryInfo(target).Name.Equals(d));
}
//then use this
var filteredDirs = Directory.GetDirectories(path).Where(d => !isExcluded(_excludedDirectories, d));
the opposite of (A || B) is (!A && !B), so in your code it should be &&, not ||...

Creating a list of docs that contains same name

I'm creating a tool that is supposed to concatenate docs that contain the same name.
example: C_BA_20000_1.pdf and C_BA_20000_2.pdf
These files should be grouped in one list.
That tool runs on a directory lets say
//directory of pdf files
DirectoryInfo dirInfo = new DirectoryInfo(#"C:\Users\derp\Desktop");
FileInfo[] fileInfos = dirInfo.GetFiles("*.pdf");
foreach (FileInfo info in fileInfos)
I want to create an ArrayList that contains filenames of the same name
ArrayList list = new ArrayList();
list.Add(info.FullName);
and then have a list that contains all the ArrayLists of similar docs.
List<ArrayList> bigList = new List<ArrayList>();
So my question, how can I group files that contains same name and put them in the same list.
EDIT:
Files have the same pattern in their names AB_CDEFG_i
where i is a number and can be from 1-n. Files with the same name should have only different number at the end.
AB_CDEFG_1
AB_CDEFG_2
HI_JKLM_1
Output should be:
List 1: AB_CDEFG_1 and AB_CDEFG_2
List 2: HI_JKLM_1
Create method which extracts 'same' part of file name. E.g.
public string GetRawName(string fileName)
{
int index = fileName.LastIndexOf("_");
return fileName.Substring(0, index);
}
And use this method for grouping:
var bigList = Directory.EnumerateFiles(#"C:\Users\derp\Desktop", "*.pdf")
.GroupBy(file => GetRawName(file))
.Select(g => g.ToList())
.ToList();
This will return List<List<string>> (without ArrayList).
UPDATE Here is regular expression, which will work with all kind of files, whether they have number at the end, or not
public string GetRawName(string file)
{
string name = Path.GetFileNameWithoutExtension(file);
return Regex.Replace(name, #"(_\d+)?$", "")
}
Grouping:
var bigList = Directory.EnumerateFiles(#"C:\Users\derp\Desktop", "*.pdf")
.GroupBy(GetRawName)
.Select(g => g.ToList())
.ToList();
It sounds like the difficulty is in deciding which files are the same.
static string KeyFromFileName(string file)
{
// Convert from "C_BA_20000_2" to "C_BA_20000"
return file.Substring(0, file.LastIndexOf("_"));
// Note: This assumes there is an _ in the filename.
}
Then you can use this LINQ to build a list of fileSets.
using System.Linq; // Near top of file
var files = Directory.GetFiles(#"C:\Users\derp\Desktop", "*.pdf")
var fileSets = files
.Select(file => file.FullName)
.GroupBy(KeyFromFileName)
.Select(g => new {g.Key, Files = g.ToList()}
.ToList();
Aside from the fact that your question doesnt identify what "same name" means. This is a typical solution.
fileInfos.GroupBy ( f => f.FullName )
.Select( grp => grp.ToList() ).ToList();
This will get you a list of lists... also won't throw an exception if a file doesn't contain the underscore, etc.
private string GetKey(FileInfo fi)
{
var index = fi.Name.LastIndexOf('_');
return index == -1 ? Path.GetFileNameWithoutExtension(fi.Name)
: fi.Name.Substring(0, index);
}
var bigList = fileInfos.GroupBy(GetKey)
.Select(x => x.ToList())
.ToList();

Categories

Resources