Nested foreach to Linq [duplicate] - c#

This question already has answers here:
Nested ForEach loop to Linq
(4 answers)
Closed 2 months ago.
I am new to LINQ, help me convert this nested line of codes to LINQ.
foreach(var rule in rules)
{
foreach(var package in packages)
{
if(rule.KeyFrom == package.Key || rule.KeyTo == package.Key)
{
filteredRule.Add(new RuleModel{Package = new List<string>{rule.KeyTo, rule.KeyFrom}, Rule = rule.Rule});
}
}
}
Attempted query:
rules.SelectMany(r => packages.Select(p => p.Key == r.KeyFrom || p.Key == r.KeyTo))
.Select(new RuleModel {
Package = new List<string>{ r.Keyfrom, r.KeyTo},
Rule = r.Rule
}));

You are almost there. Your problem is that in the SelectMany you used Select for filtering insead of using Where:
rules.SelectMany(r => packages.Where(p => p.Key == r.KeyFrom || p.Key == r.KeyTo))
.Select(r => new RuleModel {
Package = new List<string>{ r.Keyfrom, r.KeyTo},
Rule = r.Rule
}));
Where - Filters a sequence of values based on a predicate
Select - Projects each element of a sequence into a new form

You have to use SelectMany.
The code will be like this:
var ruleModels = rules.SelectMany(r => r.packages).Where(rule.KeyFrom == package.Key || rule.KeyTo == package.Key).Select(r => new RuleModel{Package = new List<string>{r.KeyTo, r.KeyFrom}, Rule = r.Rule});
filteredRule.AddRange(ruleModels);

You may try the below code:
filteredRule.AddRange(rules.Where(b =>
packages.Any(a => b.KeyFrom == a.Key || b.KeyTo == a.Key)).select(
p=>new new RuleModel { Package = new List<string>
{ p.KeyTo, p.KeyFrom }, Rule = p.Rule }));

Related

C# LINQ How to find matching item from one list in another list and check for the property in the other list

I would like to loop through a list of item,
find the matching record in another list and
check a property of that item in the new list is enabled/disabled.
if false, break the looping.
Here is my code.
var isDataPresent = true;
foreach (var item in listA)
{
var dataPresent = listB
.FirstOrDefault(x => x.Id == item.Id &&
x.DistributionType == item.DistributionType)
?.IsDataPresent;
if (!dataPresent.GetValueOrDefault())
{
isDataPresent = false;
break;
}
}
return isDataPresent;
Is there a faster/better way to achieve this.
My updated code with JOIN:
var result = listA.Join(listB,
dt => new { dt.Id, dt.DistributionType },
dp => new { dp.Id, dp.DistributionType },
(distType, dataPresent) => new
{
distType.Id,
distType.DistributionType,
dataPresent.IsDataPresent
});
if(result.Any(x => x.IsDataPresent == false))
{
isDataPresent = false;
}
you can change your use of FirstOrDefault() to Any()
that way, you don't need to make other checks and you only need one variable.
var isDataPresent = true;
foreach (var item in listA)
{
isDataPresent = listB.Any(x => x.Id == item.Id &&
x.DistributionType == item.DistributionType && x.IsDataPresent);
if (!isDataPresent)
break;
}
return isDataPresent;
Try with Any like below.
var isDataPresent = !listA.Any(item => !listB
.FirstOrDefault(x => x.Id == item.Id && x.DistributionType == item.DistributionType)
?.IsDataPresent.GetValueOrDefault());
my approach would be .Count()
var countOfA = listA.Count();
var countOfAWithB = listA.Count(a=> listB.Any(b => a.Id == b.Id &&
a.DistributionType == b.DistributionType));
return countOfA == countOfAWithB;
You can achieve by join:
var res = (from dp in dataPresent
from la in listA
where dp.DistributionType == la.DistributionType
select new {
la.isDataPresent }
).ToList()
and there are better solution then join

How do I create and populate a dynamic object using a dynamically built lambda expression

I'm trying to create and populate a dynamic object from a dataset that is only known at run time. In the code below, I create my IEnumerable results from my dataset with some known fields (ID, Primary Data, DisplayOrder, IsActive) and one user-define field (Phone Number) which I won't know at design time, and therefore must be built dynamically. The code below works, but only because I've hard-coded the dynamic field Phone Number. How do I build the Lambda expression dynamically to handle those fields only known at runtime. I want the equivalent of
string fieldName = 'PhoneNumber = ';
string fieldSource = 'pd.tbList_DataText';
string expression = 'pd=>new { ID = pd.ID, PrimaryData=pd.PrimaryData';
expression += fieldName;
expression += FieldSource;
expression += '.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()})';
var results = primaryData.Select(expression);
So how do I make that work? Thanks
// Get base Data
IEnumerable<tbList_Data> primaryData = await _tbList_DataRepository.GetAsync(ld => ld.ListID == listId && (ld.IsActive == includeInactive ? ld.IsActive : true));
// Get Final Results
var results = primaryData.Select(pd => new {
Id = pd.ID,
PrimaryData = pd.PrimaryData,
PhoneNumber = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()
});
I see several options.
1) Tuple
var results = primaryData.Select(pd => new {
Id = pd.ID,
PrimaryData = pd.PrimaryData,
Extra = Tuple.Create("PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First())
});
// How to access:
foreach (var result in results)
{
Console.WriteLine(result.Id);
Console.WriteLine(result.PrimaryData);
Console.WriteLine(result.Extra.Item1);
Console.WriteLine(result.Extra.Item2);
}
2) Dictionary
// Using C# 6 notation
var results = primaryData.Select(pd => new Dictionary<string, object>{
["Id"] = pd.ID,
["PrimaryData"] = pd.PrimaryData,
["PhoneNumber"] = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()
});
// Using C# 5 notation
var results = primaryData.Select(pd => new Dictionary<string, object>{
{"Id", pd.ID},
{"PrimaryData", pd.PrimaryData},
{"PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()}
});
// How to access:
foreach(var result in results)
{
Console.WriteLine(result["Id"]);
Console.WriteLine(result["PrimaryData"]);
Console.WriteLine(result["PhoneNumber"]);
}
3) Dynamic
var results = primaryData.Select(pd => {
dynamic result = new System.Dynamic.ExpandoObject();
result.Id = pd.ID;
result.PrimaryData = pd.PrimaryData;
// Choose one of the following. Since you only "PhoneNumber" at runtime, probably the second one.
result.PhoneNumber = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First();
((IDictionary<string, object>)result).Add("PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First());
return result;
});
// How to access:
foreach(var result in results)
{
Console.WriteLine(result.Id);
Console.WriteLine(result.PrimaryData);
// Both work, independently how you created them
Console.WriteLine(result.PhoneNumber);
Console.WriteLine(((IDictionary<string, object>)result)["PhoneNumber"]);
}
EDIT: just realized from question that the field source should be dynamic as well. So, in the above code, replace any occurrence of pb.tbList_DataText by:
((IEnumerable<X>)pb.GetType().GetField("tbList_DataText").GetValue(pb))
Where X should be the type of ld. But carefull! This cast can potentially fail.
Also, if you want a property instead of a field, just use GetProperty instead of GetField.

Linq Combine two statements into one big statement (optimization)

I have a method which is using a lot of LINQ to set and match some values in a list of Tuple<string, int>.
Right now i'm still stuck with two foreach loops nested into eachother and i think it'd be possible to combine them into one gigantic LINQ query. I'm wondering what would be the best way to do this with optimization as a big condition.
This is the function i'm talking about:
private async void AddLocalChangesFromPendingOperations()
{
var pendingOperations = await this.operationsStorage.GetOperationsAsync();
var list = pendingOperations.
SelectMany(pendingOperation =>
pendingOperation.Settings, (pendingOperation, setting) =>
new { pendingOperation, setting })
.Where(a => a.setting.Key == "selection")
.Select(a => new Tuple<string, int>(
a.pendingOperation.DefinitionId,
Convert.ToInt32(a.setting.Value.ValueObject)))
.ToList();
foreach (var pendingChange in list)
{
var selection = await this.selectionsStorage.GetSelectionByIdAsync(pendingChange.Item2);
foreach (var selectionsViewModel in this.SelectionsList.Where(a => a.Name == selection.Name))
{
if (pendingChange.Item1 == "selection-add-animals")
{
selectionsViewModel.IsActive = true;
}
else if (pendingChange.Item1 == "selection-remove-animals")
{
selectionsViewModel.IsActive = false;
}
}
}
}
If possible i'd like to optimize the last two foreaches while using linq. I've tried something but i'm stuck on setting values in the current list...
I was doing this:
this.SelectionsList = this
.SelectionsList
.Where(a => a.Name == selection.Name)
.SingleOrDefault(
a => pendingChange.Item1 == "selection-add-animals" ? a.IsActive = true : a.IsActive = false
);
In general, LINQ is for querying items (Language Integrated Query). You could however do a query and then do a foreach at the end:
private async void AddLocalChangesFromPendingOperations()
{
var pendingOperations = await this.operationsStorage.GetOperationsAsync();
(await Task.WhenAll(pendingOperations
.SelectMany(pendingOperation =>
pendingOperation.Settings, (pendingOperation, setting) =>
new { pendingOperation, setting })
.Where(a => a.setting.Key == "selection")
.Select(a => Tuple.Create(a.pendingOperation.DefinitionId, Convert.ToInt32(a.setting.Value.ValueObject)))
.Select(async pendingChange => Tuple.Create(await this.selectionsStorage.GetSelectionByIdAsync(pendingChange.Item2)), pendingChange))
.SelectMany(tuple => this.SelectionsList.Where(a => a.Name == tuple.Item1.Name)
.Select(selectionsViewModel => Tuple.Create(selectionsViewModel, tuple.Item2))
.Select(tuple => Tuple.Create(tuple.Item1, tuple.Item2.Item1 == "selection-add-animals"))
.ToList()
.ForEach(tuple => tuple.Item1.IsActive = tuple.Item2);
}
Whether this is clearer than your original implementation is up for discussion (I don't think it is), but it would be one way of doing it.
NOTE: This was typed into the editor directly, there might be some minor syntax errors.
You can do something like:
this.SelectionsList = this.SelectionsList
.Where(a => a.Name == selection.Name)
.Select(a =>
{
a.IsActive = a.Name == selection.Name ? true:false;
return a;
}).ToList();

Lambda expression where id in list of ids

I have 2 lists
var listquestionold = db.tblExamQuetions.Where(p => p.QuetionExamId == oldexamid).ToList();
var listquestionnew = listquestionnew = db.tblExamQuetions.Where(p => p.QuetionExamId == examid ).ToList();
List<tblExamQuestionAnswers> listanswers = new List<tblExamQuestionAnswers>();
How can I get answers where questionId is in listquestionold:
listanswers =db.tblanswers.where(p=> p.ExamQuestionId exists in listquestionold ?
It's easy with Contains method of the List:
var listquestionold = db.tblExamQuetions.Where(p => p.QuetionExamId == oldexamid).ToList();
var listanswers = db.tblanswers.Where(w => listquestionold.Contains(w.ExamQuestionId)).ToList();

Dynamically build IQueryable OR Statement

I am trying to dynamically build an IQueryable that will test to see if a number of strings exists in the description.
using SQL this can be easily achieved by having multiple OR statements.
but how can i do this using LINQ?
here is my code thus far...
List<string> keywords = new List<string>() { "BMW", "M3" };
IQueryable<AuctionVehicle> Results =
from a in DBContext.tbl_Auction
join v in DBContext.tbl_Vehicle on a.VehicleID equals v.VehicleID
where v.Fuel.Equals(Fuel)
&& v.Transmission.Equals(Transmission)
&& a.EndDate < DateTime.Now
select new AuctionVehicle()
{
DB_Auction = a,
DB_Vehicle = v
};
// Keywords
if (keywords.Count == 1)
{
Results = Results.Where(x => x.DB_Auction.Description.Contains(keywords[0]));
}
else if (keywords.Count > 1)
{
// ****************************
// How can i add multiple OR statements??
// ****************************
Results = Results.Where(x => x.DB_Auction.Description.Contains(keywords[0]));
foreach (string keyword in keywords)
{
Results = Results.Where(x => x.DB_Auction.Description.Contains(keyword));
}
}
return Results;
You can replace:
if (keywords.Count == 1)
{
Results = Results.Where(x => x.DB_Auction.Description.Contains(keywords[0]));
}
else if (keywords.Count > 1)
{
Results = Results.Where(x => x.DB_Auction.Description.Contains(keywords[0]));
foreach (string keyword in keywords)
{
Results = Results.Where(x => x.DB_Auction.Description.Contains(keyword));
}
}
by
Results = Results.Where(x => keywords.Any(y=>x.DB_Auction.Description.Contains(y)));
So, eventually you may want to add this in you LINQ expression:
where keywords.Any(x=>a.Description.Contains(x))

Categories