How can I check if folders are empty? - c#

public Form1()
{
InitializeComponent();
if (textBoxRadarPath.Text != "")
{
if (!Directory.Exists(textBoxRadarPath.Text))
{
Directory.CreateDirectory(textBoxRadarPath.Text);
btnStart.Enabled = true;
}
}
if (textBoxSatellitePath.Text != "")
{
if (!Directory.Exists(textBoxSatellitePath.Text))
{
Directory.CreateDirectory(textBoxSatellitePath.Text);
btnStart.Enabled = true;
}
}
CheckIfImagesExist();
}
I'm checking if the folders in textBoxRadarPath.Text and textBoxSatellitePath.Text exist and create them if not. Then I call the method CheckIfImagesExist();
The problem is in the folder CheckIfImagesExist(); I'm also trying to get files :
There is more code in the method but the important is the GetImagesFiles method
private void CheckIfImagesExist()
{
GetImagesFiles();
}
And in GetImagesFiles
private void GetImagesFiles()
{
if (textBoxRadarPath.Text != "" || textBoxSatellitePath.Text != "")
{
if (Directory.Exists(textBoxRadarPath.Text))
{
if (checkBoxGetImages)
{
filesRadar = System.IO.Directory.GetFiles(textBoxRadarPath.Text,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
else
{
var t = new DirectoryInfo(textBoxRadarPath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
filesRadar = System.IO.Directory.GetFiles(t.FullName,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
Array.Sort(filesRadar, new MyComparer(true));
}
if (Directory.Exists(textBoxSatellitePath.Text))
{
if (checkBoxGetImages)
{
filesSatellite = System.IO.Directory.GetFiles(textBoxSatellitePath.Text,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
else
{
var t = new DirectoryInfo(textBoxSatellitePath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
filesSatellite = System.IO.Directory.GetFiles(t.FullName,
"*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray();
}
Array.Sort(filesSatellite, new MyComparer(false));
}
}
}
Here I'm trying to get images from the first child folder under textBoxRadarPath.Text and textBoxSatellitePath.Text
The problem is if the directories textBoxRadarPath.Text and textBoxSatellitePath.Text not exist so they have created but they are empty yet so I'm getting exception on the line
var t = new DirectoryInfo(textBoxRadarPath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
Because the two directories just created or they are empty.
How can I check in the constructor if the directories are empty don't call the method CheckIfImagesExist() ?
Only if there are child folders inside then call the method CheckIfImagesExist()
The exception message :
System.InvalidOperationException: 'Sequence contains no elements'
On the line :
var t = new DirectoryInfo(textBoxRadarPath.Text).GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc).First();
This screenshot show example of the folders structure on the hard disk.

Use FirstOrDefault when you are not sure there will be a resulting item
var t = new DirectoryInfo(textBoxRadarPath.Text)
.GetDirectories()
.OrderByDescending(d => d.LastWriteTimeUtc)
.FirstOrDefault(); // <- this will return null if there are no entries
then before you start checking the files, make sure you found a directory.
if (t != null)
{
filesSatellite = System.IO.Directory
.GetFiles(t.FullName, "*.gif", SearchOption.AllDirectories)
.OrderBy(x => x)
.ToArray();
}

Related

Getting counter to return correct number after being recursively called c#

I have a method called clearFiles which can be called recursively if there is sub directories. I have created a counter that will count the number of files that have been deleted. The counter holds the correct number when the function is either called once or called recursively but the toast at the end of the function only returns the correct number when it is not called recursively. Is there anyway I can display the toast and then reset the number of filesCleared? Because it's just returning a 0 when it's called recursively.
From playing around with it for a bit it seems like the toast is getting called after the filesCleared variable is set to 0 which is not what I want.
filesCleared variable:
int filesCleared = 0;
clearFiles:
public async Task ClearFiles()
{
var pathName = FileFilter.PathName;
FileInfo[] files = SortFiles(pathName);
try
{
if(FileFilter.Filter == "all")
{
foreach(var file in files)
{
if(file.Extension == FileFilter.Extension || FileFilter.Extension == "all")
{
File.Delete(file.ToString());
filesCleared++;
}
}
}
if(FileFilter.Filter == "date")
{
foreach (var file in files) //regular files
{
if(file.CreationTime < FileFilter.DeleteDate) //based on time
{
if(file.Extension == FileFilter.Extension || FileFilter.Extension == "all") //based on extension
{
File.Delete(file.ToString());
filesCleared++;
}
}
}
}
if(FileFilter.Filter == "number")
{
var i = 0;
for(var j = files.Length-1; j >= 0 ; j--)
{
if(files[j].Extension == FileFilter.Extension || FileFilter.Extension == "all")
{
if(i++ >= FileFilter.FilesToKeep)
{
File.Delete(files[j].ToString());
filesCleared++;
}
}
}
}
if (FileFilter.SubFolders == true) //subfiles (will be called recursively w/ each filter)
{
foreach(var subDir in new DirectoryInfo(pathName).GetDirectories())
{
//subDir.Delete(true);
FileFilter.PathName = subDir.ToString();
ClearFiles();
//await ClearFiles(subDir.ToString());
}
FileFilter.PathName = pathName; //resets the pathName so it will go back to what it was before the recursion
}
}
catch (IOException ioExp)
{
Console.WriteLine(ioExp.Message);
Toast = Toast.Bad();
logger.LogError(ioExp, "Error Deleting");
}
Toast = Toast.Good(filesCleared + " Files Deleted");
filesCleared = 0;
}
If you want to do something once but also want to call a method recursively, you have to split it in two. After trying to simplify your code I get a ClearFiles method like this:
public void ClearFiles()
{
var filesCleared = 0;
try
{
filesCleared = DeleteFilesRecursively(FileFilter.PathName, FileFilter);
}
catch (IOException ioExp)
{
Console.WriteLine(ioExp.Message);
Toast = Toast.Bad();
logger.LogError(ioExp, "Error Deleting");
}
Toast = Toast.Good(filesCleared + " Files Deleted");
}
Now Toast.Good is only called once after all subfolders have been traversed.
Note that filesCleared is a local variable, since I don't see any point in making it global. That way you also don't need to reset it.
The implementation of DeleteFilesRecursively could be something like this and could be simplified more if you wanted:
private const string All = "all";
private const string FilterByDate = "date";
private const string FilterByNumber = "number";
int DeleteFilesRecursively(string dirPath, SomeFileFilterType fileFilter)
{
FileInfo[] files = SortFiles(dirPath);
var deleted = 0;
var toBeDeleted = files.Where(f => MatchesByExtension(f, fileFilter.Extension));
if (fileFilter.Filter == FilterByDate)
{
toBeDeleted = toBeDeleted.Where(f => MatchesByDate(f, fileFilter.DeleteDate));
}
else if (FileFilter.Filter == FilterByNumber)
{
// If your SortFiles method sorted in the other
// direction this call to Reverse would not be needed.
toBeDeleted = toBeDeleted.Reverse().Take(fileFilter.FilesToKeep);
}
foreach (var file in toBeDeleted)
{
File.Delete(file.ToString());
deleted++;
}
if (fileFilter.SubFolders)
{
foreach(var subDir in new DirectoryInfo(dirPath).GetDirectories())
{
deleted += DeleteFilesRecursively(subDir.FullName, fileFilter);
}
}
return deleted;
}
bool MatchesByExtension(FileInfo file, string extension)
=> file.Extension == extension || extension == All;
bool MatchesByDate(FileInfo file, DateTime deleteDate)
=> file.CreationTime < deleteDate;
Note that I also removed your magic strings, which could be even better by replacing them with an enum type.
I haven't tested this but I believe it should give you the same behavior as your current code (at least the parts about filtering and deleting).

comparing current list data with old list data

I have to compare current list of data with previous list of data. In my list class i have
Id,
Comment,
Updatedby,
Updated Dt,
IsEdited
and few other fields. I am comparing like below.
foreach (var cscData in Currentcloses)
{
if (cscData.Status == ReferenceEnums.ActionStatus.EDIT)
{
if (Previoussoftcloses != null)
{
foreach (var pscData in Previouscloses)
{
if (cscData.Id == pscData.Id)
{
//my logic goes here
}
}
}
}
}
Is there any better way other than this. Just want to check.
New Code
var currentData = Currentsoftcloses
.Where(c => c.Status == ReferenceEnums.ActionStatus.EDIT);
foreach (var cscData in currentData)
{
if (Previoussoftcloses != null)
{
var previous = Previoussoftcloses
.GroupBy(item => item.Id)
.ToDictionary(chunk => chunk.Key, chunk => chunk.First());
if (previous.TryGetValue(cscData.Id, out var pscData))
{
//my logic goes here
}
} }
You can get rid of inner loop; if Previouscloses is long, your code will be faster: O(|Previoussoftcloses| + |Currentcloses|) versus O(|Previoussoftcloses| * |Currentcloses|) time complexity.
// If Previoussoftcloses == null we can do nothing, let's check for this possibility
if (Previoussoftcloses != null) {
// Dictionary is faster then List on Contains O(1) vs. O(N)
var previous = Previouscloses
.GroupBy(item => item.Id)
.ToDictionary(chunk => chunk.Key, chunk => chunk.First());
// Readability: what we are going to scan
var current = Currentcloses
.Where(c => c.Status == ReferenceEnums.ActionStatus.EDIT);
foreach (var cscData in current) {
if (previous.TryGetValue(cscData.Id, out var pscData)) {
//my logic goes here
}
}
}

Extract common path from a collection of grouped file paths?

I have a question related to https://en.wikipedia.org/wiki/Longest_common_substring_problem, my source collection contains a list of file paths that doesn't always share a common path (outside of the C:\ drive sometimes) ex:
Source collection :
C:\Test\Root\Common\Data\a.txt
C:\Test\Root\Common\Data\Home\b.txt
C:\Test\Root\Common\Data\Home\Dev\c.txt
C:\Test2\Random\Data\a.txt
C:\Test2\Random\b.txt
C:\Test2\c.txt
D:\Data\a.txt
Output should be a collection :
C:\Test\Root\Common\Data\
C:\Test2\
D:\Data\
How to find the common path of each "group" of file paths ? I've found many solutions here but it is always with a collection of file paths sharing at least one common directory which is not the case here.
I am still not sure that I understand problem correctly...
I hope this will work.
public List<string> ExtractCommonPaths(List<string> paths)
{
var separatedImput = paths
.Select(path => path.Split(new [] {":\\", "\\" }, StringSplitOptions.RemoveEmptyEntries))
.Select(path => path.Take(path.Length - 1).ToList());
return separatedImput.GroupBy(path => path[0] + ":\\" + path[1])
.Select(g =>
{
var commonPath = g.Key;
var commpoPathLength = 2;
for (;;)
{
var exit = false;
var pathItem = string.Empty;
foreach (var path in g)
{
if (path.Count <= commpoPathLength)
{
exit = true;
break;
}
if (pathItem == string.Empty)
pathItem = path[commpoPathLength];
else
{
if (pathItem != path[commpoPathLength])
{
exit = true;
break;
}
}
}
if (exit)
break;
commonPath += "\\" + pathItem;
commpoPathLength++;
}
return commonPath;
})
.ToList();
}
I have something if you want to look for the directories inside some location.
To this method i have every of directories from some (C:\Files1) example location.
If you want to get only main directories from this list:
public List<DirectoryInfo> ExtractDirectoriesCommonPaths(List<DirectoryInfo> directories, string location)
{
var result = new List<DirectoryInfo>() { };
directories.ForEach(directory =>
{
var otherDirectories = directories.Where(d => d.FullName != directory.FullName);
var anyDirectoryWithThisSamePath = otherDirectories.Where(x => directory.FullName.Contains(x.FullName) && x.FullName.Length < directory.FullName.Length);
if (anyDirectoryWithThisSamePath.Any())
{
result.Add(anyDirectoryWithThisSamePath.FirstOrDefault());
}
});
return result.Where(x => x.FullName != location && x.FullName.Length > location.Length).Distinct().ToList();
}
Input:
C:\Files1\test_announcements
C:\Files1\Parent
C:\Files1\test_announcements\test_announcements_archive
C:\Files1\Parent\child
C:\Files1\Parent\child2
C:\Files1\Parent\child\subchild
C:\Files1\test_announcements
C:\Files1\Parent
Output:
C:\Files1\test_announcements
C:\Files1\Parent

How to assign "var" inside if statement

I need to do this:
var productsLocation = response.blah blah; //with linq query
var item; // even var item = null; //not valid
if(condition){
item = productsLocation.linq query
} else {
item = productsLocation.differentquery
}
var group = item.query;
Is this possible? If yes, how?
EDIT: here is my exact code:
var productLocation = response.productLocation.Select(p => ProductLocationStaticClass.DtoToModel(p));
var items;
if (condition)
{
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName));
} else {
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName) && stocks.Contains(s.Barcode));
}
If you look closely at the logic, you notice you don't actually even need the if block. The whole thing can be written in one expression as follows:
var items = productLocation
.Select(s => new ProductClass(s))
.Where(s => categories.Contains(s.CategoryName) && (condition || stocks.Contains(s.Barcode)));
First of all get your response variable type, then initialize 'item' variable as IEnumarable where T is same as response variable type
var productsLocation = response.productLocation.Select(p => ProductLocationStaticClass.DtoToModel(p));
IEnumerable<ProductClass> item;
if (condition)
{
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName));
}
else
{
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName) && stocks.Contains(s.Barcode));
}
var group = item.Where(condition);
You can do it with IEnumerable interface in this way:
using System.Collections;
using System.Collections.Generic;
List<string> products = new List<string>() { "First", "Second", "Third", "Fourth" };
IEnumerable item;
var condition = false;
if (condition)
{
item = products.Select(x=>x);
}
else
{
item = products.Where(x => x.StartsWith("F"));
}
var group = item.Cast<string>().Where(/*...Here your conditions...*/)

Refactor methods - pass in property name

I'm wondering if it's possible to refactor these two methods into one. The only difference is in the Select method; one uses BaselineSetNumber, the other ComparisonSetNumber.
public Set LoadBaselineSet(ObservableCollection<Set> sets)
{
using (var db = _contextFactory.GetContext())
{
var setNumber =
db.Users.Where(x => x.Login == Environment.UserName)
.Select(x => x.BaselineSetNumber).Single(); // !!! HERE
return sets.Single(x => x.SetNumber == setNumber);
}
}
public Set LoadComparisonSet(ObservableCollection<Set> sets)
{
using (var db = _contextFactory.GetContext())
{
var setNumber =
db.Users.Where(x => x.Login == Environment.UserName)
.Select(x => x.ComparisonSetNumber).Single(); // !!! HERE
return sets.Single(x => x.SetNumber == setNumber);
}
}
I'd like to have a method that I can call like LoadSet(sets, BaselineSetNumber); or LoadSet(sets, ComparisonSetNumber);
Yes, this is possible by creating a higher-order function. Your new code would look something like this:
public Set LoadBaselineSet(ObservableCollection<Set> sets)
{
return LoadSet(sets, (x) => x.BaselineSetNumber)
}
public Set LoadComparisonSet(ObservableCollection<Set> sets)
{
return LoadSet(sets, (x) => x.ComparisonSetNumber)
}
public Set LoadSet(ObservableCollection<Set> sets, Func<dbObject, Int> elementIdentity){
using (var db = _contextFactory.GetContext())
{
var setNumber =
db.Users.Where(x => x.Login == Environment.UserName)
.Select(elementIdentity).Single(); // !!! HERE
return sets.Single(x => x.SetNumber == setNumber);
}
}

Categories