Find String matches any in List of Strings - c#

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();

Related

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))){

Linq intersect to filter multiple criteria against list

I'm trying to filter users by department. The filter may contain multiple departments, the users may belong to multiple departments (n:m). I'm fiddling around with LINQ, but can't find the solution. Following example code uses simplified Tuples just to make it runnable, of course there are some real user objects.
Also on CSSharpPad, so you have some runnable code: http://csharppad.com/gist/34be3e2dd121ffc161c4
string Filter = "Dep1"; //can also contain multiple filters
var users = new List<Tuple<string, string>>
{
Tuple.Create("Meyer", "Dep1"),
Tuple.Create("Jackson", "Dep2"),
Tuple.Create("Green", "Dep1;Dep2"),
Tuple.Create("Brown", "Dep1")
};
//this is the line I can't get to work like I want to
var tuplets = users.Where(u => u.Item2.Intersect(Filter).Any());
if (tuplets.Distinct().ToList().Count > 0)
{
foreach (var item in tuplets) Console.WriteLine(item.ToString());
}
else
{
Console.WriteLine("No results");
}
Right now it returns:
(Meyer, Dep1)
(Jackson, Dep2)
(Green, Dep1;Dep2)
(Brown, Dep1)
What I would want it to return is: Meyer,Green,Brown. If Filter would be set to "Dep1;Dep2" I would want to do an or-comparison and find *Meyer,Jackson,Green,Brown" (as well as distinct, as I don't want Green twice). If Filter would be set to "Dep2" I would only want to have Jackson, Green. I also played around with .Split(';'), but it got me nowhere.
Am I making sense? I have Users with single/multiple departments and want filtering for those departments. In my output I want to have all users from the specified department(s). The LINQ-magic is not so strong on me.
Since string implements IEnumerable, what you're doing right now is an Intersect on a IEnumerable<char> (i.e. you're checking each letter in the string). You need to split on ; both on Item2 and Filter and intersect those.
var tuplets = users.Where(u =>
u.Item2.Split(new []{';'})
.Intersect(Filter.Split(new []{';'}))
.Any());
string[] Filter = {"Dep1","Dep2"}; //Easier if this is an enumerable
var users = new List<Tuple<string, string>>
{
Tuple.Create("Meyer", "Dep1"),
Tuple.Create("Jackson", "Dep2"),
Tuple.Create("Green", "Dep1;Dep2"),
Tuple.Create("Brown", "Dep1")
};
//I would use Any/Split/Contains
var tuplets = users.Where(u => Filter.Any(y=> u.Item2.Split(';').Contains(y)));
if (tuplets.Distinct().ToList().Count > 0)
{
foreach (var item in tuplets) Console.WriteLine(item.ToString());
}
else
{
Console.WriteLine("No results");
}
In addition to the other answers, the Contains extension method may also be a good fit for what you're trying to do if you're matching on a value:
var result = list.Where(x => filter.Contains(x.Value));
Otherwise, the Any method will accept a delegate:
var result = list.Where(x => filter.Any(y => y.Value == x.Value));

starts with over an array

I have a function like so
public List<Entry> GetEntriesForSlider(int first, int max, List<string> NameLetters)
{
//Some code
}
Inside this code, I want to search along a database, to return every result that has the firstname starting with a letter within the NameLetters.
So if I pass in the array NameLetters = ["a","b","c"]
Then it will return results such as
Amy
Bert
Aaron
Chris
It should be noted that I am ideally looking to use some sort of linq statement such as...
entries.Where(x => x.FirstName.StartsWith(partofArray));
If at all possible.
EDIT : I previously had the following :
var entries = _repository.All<Entry>().Skip(first).Take(max);
if (NameLetters != null && NameLetters.Count > 0)
entries = entries.Where(x => NameLetters.Contains(x.FirstName[0].ToString()));
But what I found was, it enumerated the query (I think) before running the where statement. Possibly because trying to access the first letter of firstname (Or the ToString).
If you're just looking to match the first letter try:
entries.Where(x => partofArray.Contains(x.FirstName[0]));
If you need to worry about null or empty strings a safer version would be:
entries.Where(x =>
!string.IsNullOrEmpty(x.FirstName) &&
partofArray.Contains(x.FirstName[0])
);
If you want to use variable-length strings try:
entries.Where(x =>
partofArray.Any( p=> x.FirstName.StartsWith(p))
);
Perhaps you should take a look at predicate builder.
Then something like
IQueryable<Entry> GetThingsThatBeginWithA(String[] prefixes)
{
var predicate = PredicateBuilder.False<Entry>();
foreach (String prefix in prefixes)
predicate = predicate.Or(p => p.FirstName.StartsWith(prefix));
return database.Entries.Where(predicate);
}
This query should correctly compile to a single query to the SQL store.
Update: It can also be done without predicate builder, but I find that working with expression trees without it is really tedious.

C# Comparison operators not supported for type Dictionary<string,string>

I get a dictionary and i want to get the matching database entries for the key field in the dictionary.
So the code below gets all database fields that equals the data in the dictionary.keys. So far so good. Now when i loop it and i try to get the field from the fields that matches the key x it fails with an exception:
{"Comparison operators not supported for type 'System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.String]'"}
var fields = _dc.fieldInfos.Where(x => x.name.Equals(param.MethodData.Keys));
foreach (var entry in param.MethodData)
{
KeyValuePair<string, string> entry1 = entry;
var field = fields.SingleOrDefault(x => x.name.Equals(entry1.Key));
if (field == null)
....
}
The line that fails is this:
var field = fields.SingleOrDefault(x => x.name.Equals(entry1.Key));
Any ideas?
Be mindful that the _dc.fieldInfos.Where(x => x.name.Equals(param.MethodData.Keys)) (line 1) doesn't actually get executed until you try to iterate over fields (line 5).
At that point, x.name.Equals(param.MethodData.Keys) fails because a string (x.name) cannot be compared to KeyCollection<string> (param.MethodData.Keys).
Essentially, you are getting the exception on unexpected line because of the deferred execution.
This is because the LINQ to SQL provider cannot translate the C# expression passed to the SingleOrDefault method into an SQL statement.
You can easily solve this problem by having LINQ to SQL execute the fields query before filtering it further with the SingleOrDefault method. This way the second operation will happen on the collection of objects in memory instead of the database:
var field = fields.ToList().SingleOrDefault(x => x.name.Equals(entry1.Key));
Related resources:
LINQ and Deferred Execution
Are you sure it's not this line that's failing:
var fields = _dc.fieldInfos.Where(x => x.name.Equals(param.MethodData.Keys));
as name isn't going to match a collection of strings.
which if so, I'd rewrite as:
var fields = _dc.fieldInfos.Where(x => param.MethodData.Keys.Contains(x.name));
entry1.key is a string, so I can't see why the line that you highlighted would fail with that message.
var fields = _dc.fieldInfos.Where(x => x.name.Equals(param.MethodData.Keys));
This retrieves (actually filters with deferred execution) fieldInfos by matching their name to a collection of keys. I don't know what fieldInfo.Name and param.MethodData.Keys mean, but i assume it's not what you want.
I'd suggest something along the lines of:
var pairs = param.MethodData.Join(_dc.fieldInfos,
kvp => kvp.Key,
fi => fi.name,
(kvp, fi) => new { Key = kvp.Key, FieldInfo = fi });
foreach (var pair in pairs)
param.MethodData[pair.Key] = pair.FieldInfo;
Im not exactly sure it's gonna work in Linq to SQL though.

How to search for any text in List<String>

I have list and I need to search for items something like:
if the user searches for smi it will bring all items that include smi?
Any idea?
Check the following example
string serachKeyword ="o";
List<string> states = new List<string>();
states.Add("Frederick");
states.Add("Germantown");
states.Add("Arlington");
states.Add("Burbank");
states.Add("Newton");
states.Add("Watertown");
states.Add("Pasadena");
states.Add("Maryland");
states.Add("Virginia");
states.Add("California");
states.Add("Nevada");
states.Add("Ohio");
List<string> searchResults = states.FindAll(s => s.Contains(serachKeyword));
This will find all results that start with 'smi' (mySearchString)
foreach(var result in myList.Where(s => s.IndexOf(mySearchString) == 0))
{
// Do whatever
}
This will find any that contains 'smi' (mySearchString)
foreach(var result in myList.Where(s => s.IndexOf(mySearchString,StringComparison.InvariantCultureIgnoreCase) != -1))
{
// Do whatever
}
This will search for your text (ignoring case), and return any strings that contain the text.
that should work - Don't have IDE close by, but hope it helps
To search items that Include 'smi'
var result = list.Where(s => s.Contains("smi"));
If you want to grab all of the items that contain "smi" anywhere, like "smith" and "vesmit":
var list = myList.Where(m => m.Contains("smi"));
If you want to grab all of the items that contain "smi" at the start of the string, like "smith", "smitten", and "smile":
var list = myList.Where(m => m.BeginsWith("smi"));
If you want more flexibility, you can use a Regex
var list = myList.Where(m => Regex.IsMatch(m, "regexPattern"));
If you use a version of C# without LINQ you can use the Find method of the List as described here (it's got quite big sample on that page too).

Categories

Resources