similar file names into array or list - c#

In each scenario I will have hundreds of .tif files that need to be merged with irfanview. These files are named so that matching character strings before a hyphen indicate they need to be merged (a small example set of file names could be as follows: 0001-1.tif, 0001-2.tif, 0001-3.tif, 0002-1.tif, 0002-2.tif, 0003.tif, 0004-1.tif, 0004-2.tif). Is there a way to put files with the same "prefix" into their own array by a means of character comparison, or would trimming the file names be easier? I would like to get all the "0001's" into an array then all the "0002's" etc. Can someone make a suggestion on the easiest way to do this?
This looks like it will work perfectly, but VS is not recognizing Substring. Is it because I'm trying to insert an array of files into the "GroupBy"? Here's what I have so far:
int i = 0;
string filmtext = textBox1.Text;
string[] filmPath = Directory.GetFiles(filmtext);
string filmfile = Path.GetFileName(filmPath[i].ToString());
filmfile.GroupBy(s => s.Substring(0,4))
.Select(g => g.ToList())
.ToList();

you could group by the first four characters:
filenames.GroupBy(s => s.Substring(0,4))
that would return a collection of groups representing the filenames within that group and a Key value being the 4 characters that you group on.
To create a list of lists from those grouping you could do:
List<List<string>> groups =
filenames.GroupBy(s => s.Substring(0,4))
.Select(g => g.ToList())
.ToList();

Related

Find String matches any in List of Strings

How to find string with a exact match in the list of string.
var inv_attr_string_id = inv_attr
.Where(x => ItemStringVal.Contains(x.STRING_VAL))
.Select(x => x.ID).ToList();
ItemStringVal Contains list of Strings like "0030", "1433", "2019" etc ... Now I am trying to match it with the database in such a way that if it match exactly and all the three strings then it returns me the list of IDs matched ... else it should return null.
I tried
List<int> inv_attr_string_id = new List<int>();
foreach (var StrItem in ItemStringVal)
{
inv_attr_string_id.AddRange
(
inv_attr.Where(x => x.STRING_VAL.Contains(StrItem))
.Select(x => x.ID).ToList()
);
}
I have tried .Any as well but I got an error saying "Internal .NET Framework Data Provider error 1025"
I was thinking if I could be able to write it the way it creates a query of AND condition such as it should match (Exactly) all the input strings.
One Liner could be: Select IDs if all the string matches. Else return null
If I understand the problem - You have a list of strings which is your Input Data. You also have a List of patterns that may match with Data. You want to find the pairs of [Data, Pattern]?
Upfront this can be solved in O(N^2).
Psuedo Logic be like:
Foreach item in DataCollection
Foreach pattern in PatternCollection
if(Regex.IsMatch(item, pattern)) Then collect the item & pattern in some place
Hope this gives some starting point for you to solve the problem.
You can try linq to get all those records from db which are exist int your list
var result = inv_attr.AsEnumerable().Where(x => ItemStringVal.Exists(y => y == x.STRING_VAL)).Select(x => x.Id).ToList();

Sort List of List and maintain correct number of lists in c#

For the following code...
var lightningFileNames = ConfigurationManager.AppSettings["LightningFileNames"];
var files = Directory.GetFiles(mapPath, lightningFileNames);
List<List<LightningStrikeModel>> ltgStrikes = new List<List<LightningStrikeModel>>();
foreach (string file in files)
{
var stringData = new List<string>();
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
var data = reader.ReadLine().Trim();
if (!string.IsNullOrEmpty(data))
{
stringData.Add(data);
}
}
reader.Close();
}
//extracted from file name to get an orderby
int lgtTemp = int.Parse(Regex.Match(file, #"\d+").Value);
ltgStrikes.Add((from item in stringData
select item.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
into rawData
where rawData.Length >= 4
select new LightningStrikeModel
{
Date = rawData[0],
Time = rawData[1],
Latitude = Convert.ToDecimal(rawData[2]),
Longitude = Convert.ToDecimal(rawData[3]),
Polarity = Convert.ToDecimal(rawData[4]),
orderBy = lgtTemp
}).ToList());
}
var tempLtg = ltgStrikes
.SelectMany(record => record)
.OrderBy(record => record.orderBy)
.GroupBy(record => record.orderBy).ToList();
return ltgStrikes;
With filenames of ltg_1.txt, ltg_2.txt ... ltg_12.txt
My problem exists because of 3 things.
1) because I am going out to a folder location to grab a list of files to read the data and populate a list, I get them in the order they are in the folder - so I would read the files in this order
_1.txt, _10.txt, _11.txt, _12.txt, _2.txt and so on
I am unable to change the files names.
2) Some of the files will have nothing in them - a blank file. But I still need to 'read it' and add a place holder to my List> ltgStrikes - i essentially need to have a list of 12 lists regardless of data.
3) Currently, I can achieve a list of 12 lists regardless of data but they are in the wrong order because its adding them to the ltgStrikes in the order they are read. so
_1.txt has an index of [0], _10.txt has an index of [1] but in the end result, it should have an index of [9], _5.txt has an index of [8] but should be [4]
I have tried something like the following but because some files are empty, I do not get a list of 12 lists. my current data only gives me a list of 2 lists since only 2 files of data in them.
var tempLtg = ltgStrikes
.SelectMany(record => record)
.OrderBy(record => record.orderBy)
.GroupBy(record => record.orderBy).ToList();
What am I not seeing? FYI - orderBy is not used to order the data here but ultimately it can be. I need it in another part of the application
You have a whole pile of problems here because you're doing stuff in the wrong order. If you're mixing loops with LINQ like this, odds are good the whole thing will be much better if you just make the whole thing into one big query with no loops. Let's do that:
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder() // You write this!
OK, now we have a sequence of files in the right order. What do we want next? The contents of the files, trimmed.
.Select(f => File.ReadLines(f)
.Select(l => l.Trim())
.Where(l => l != ""))
OK, now we have a sequence of sequences of strings. What do we want? A list of lists of LightningStrikeModels. So we transform each string into a model. That gives us a sequence of models.
.Select (stringData =>
(from item in stringData
select item.Split(new[] { ' ' },
StringSplitOptions.RemoveEmptyEntries)
into rawData
where rawData.Length >= 5
select new LightningStrikeModel (...))
We transform each sequence of models into a list of models.
.ToList())
We now have a sequence of lists of models. We want a list of lists of models:
.ToList();
And we're done. We have a list of lists of models, and we can return it.
But let's not stop there. When you're done writing the code ask yourself if you could have done better.
If we do that then we immediately see that the Trim and the filter of empty strings is completely unnecessary. Why? Because we're going to take that string, split it on spaces, eliminate the empty substrings, and discard any string that had fewer than four substrings between spaces. So why did we bother to trim the leading and trailing spaces, and eliminate empty entries? The split would do the former, and the check to see if there are four substrings does the latter. So we can make this simpler:
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder()
.Select(f => File.ReadLines(f))
.Select (stringData =>
...
Now do it again. Can we make this simpler? Yes. We have two selects in a row, so we can merge them.
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder()
.Select (f =>
(from item in File.ReadLines(f)
select item.Split(new[] { ' ' },
StringSplitOptions.RemoveEmptyEntries)
into rawData
where rawData.Length >= 5
select new LightningStrikeModel (...))
Can we make this better? OH YES WE CAN. We can make two things: a splitter helper and a factory.
static string[] SpaceSplit(this string s) => s.Split( ... );
static LightningStrikeModel BuildModel(this string[] parts) => new ...
And now our query is
return Directory.GetFiles(mapPath, lightningFileNames)
.InNaturalOrder()
.Select (f =>
File.ReadLines(f)
.Select(line => line.SpaceSplit())
.Where(rawData => rawData.Length >= 5)
.Select(rawData => rawData.BuildModel())
.ToList())
.ToList();
OMG look at how much shorter and cleaner that solution is compared to the mess we started with. Look at how clearly correct it is. And easy to understand and maintain. Always be asking yourself if you can make it better.
Can we make this solution better? Yes we can! We can notice for example that we do no error checking on whether or not the strings convert cleanly to decimals. What if they don't? That error should probably be handled somehow, but right now it is not. Think about how you might solve that in a manner that does not make the call site harder to understand.
If you want to read the files in order, you could order files in the foreach
foreach(string file in files.OrderBy(x=>int.Parse(Regex.Match(x,#"(\d+)\.txt").Groups[1].Value))){

Sort List<string> based on character count

Example:
List<string> folders = new List<string>();
folders.Add("folder1/folder2/folder3/");
folders.Add("folder1/");
folders.Add("folder1/folder2/");
I want to sort this list based on character i.e '/'
so my output will be
folder1/
folder1/folder2/
folder1/folder2/folder3
LINQ:
folders = folders.OrderBy(f => f.Length).ToList(); // consider null strings
or List.Sort
folders.Sort((s1, s2) => s1.Length.CompareTo(s2.Length));
a safe approach if the list could contain null's:
folders = folders.OrderBy(f => f?.Length ?? int.MinValue).ToList();
If you actually want to sort by the folder-depth not string length:
folders = folders.OrderBy(f => f.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Length).ToList();
It's likely you actually want to sort by name:
folders = folders.OrderBy(f => f).ToList();
Or simply:
folders.Sort();
This will work correctly for cases like this:
folder1/
folder1/subfolder1
folder1/subfolder1/subsubfolder
folder2
folder2/subfolder2
Sorting by length alone will consider "folder1" and "folder2" equal.

Linq Filter Array of Delimited Strings

I have a string like this:
RoleId,RoleName|CategoryId,CategoryName
I split them first like this:
string delm = "RoleId,RoleName|CategoryId,CategoryName";
string[] FieldsToReplace = attributes[0].IdsToReplaceWith.Split('|');
Suppose i have a variable in which i have RoleId:
string test = "RoleId";
Now what i am trying to get each the array item in which has string RoleId, i don't want to use contains i need exact match.
I have tried this query:
var test = FieldsToReplace
.Where(x=>FieldsToReplace
.All(y => y.Split(',').Equals(delm))).ToArray();
i can harcode like this for first index:
var IdProperty = FieldsToReplace.FirstOrDefault(x => x.Split(',')[0] == delm);
but i want it dynamic so it check each item of array which i got after , split.
but it returns no record.
Any help will be appreciated.
You want to split on your elements of the array. Besides that it seems appropriate to check if any element of these splitted ones are equal to your comparison string:
var test =
FieldsToReplace
.Where(x => x.Split(',')
.Any(y => y.Equals(prop.Name)))
.ToArray();

Linq Intersect with arrays

Im getting a table Tags from the db.
the table has columns ID and TagName
I'm doing something like this to get a list of strings:
var taglist = Model.Tags.Select(x => x.TagName.ToLower()).ToArray();
then I'm comparing against another string array to get the strings that occur in both:
var intersectList = tagList.Intersect(anotherList);
I have my list, but now I also want the ID of each item remaining in the intersect list that corresponds to the tagList. (can just be an int array)
Can anyone help with a good way to do this?
Don't use intersect, it only works for collections of the same type. You could do a simple join or other form of filtering. It would be easiest to throw the string list into a HashSet and filter by tags that contain TagNames in that set. This way, you keep your tags unprojected so they keep their ids and other properties.
var stringSet = anotherList.ToHashSet(StringComparer.OrdinalIgnoreCase);
var tagList = Model.Tags.Where(t => stringSet.Contains(t.TagName)).ToList();
And put them into a list. Don't throw them into an array unless you specifically need an array (for use in a method that expects an array).
Could you do:
var intersectIds = Model.Tags
.Where(tag => anotherList.Contains(tag.TagName))
.Select(tag => tag.Id)
.ToList();
Maybe use Dictionary<int, string> instead of Array?

Categories

Resources