Convert double foreach to LinQ - c#

foreach (var lg in basket)
{
foreach (var acc in lg.Accomodations)
{
if (acc.HotelID == h.ID)
{
hotel.SelectedInPreviousLeg = true;
}
}
}
I try to convert this double foreach to linq. Any suggestions?
So far i tried this, but there is a compile error.
var test = basket.FirstOrDefault(x => x.Accommodations, Any(y => y.HotelID == h.ID));
hotel.SelectedInPreviousLeg = (test != null) ? true : false;

or, assuming classes are defined equivalently to
public class Hotel
{
public int ID { get; set; }
public List<Accomodation> Accomodations { get; set; }
public bool SelectedInPreviousLeg { get set; }
}
public class Accomodation
{
public int HotelID { get; set; }
}
then you could do this:
foreach (var acc in
from lg in basket
from acc in lg.Accomodations
where acc.HotelID == h.ID
select acc)
hotel.SelectedInPreviousLeg = true;
... but that will iterate through all combinations, when you only need to find the first one that satisfies the condiditon, so you could do this, which will stop after finding the first one:
hotel.SelectedInPreviousLeg =
basket
.Any(b => b.Accomodations
.Any(a => a.HotelID == h.ID));

Related

Nested List Of Class Search Or Update Using Linq C#

How can I find class from nested list ?
I am working on trees and just want to retreive and add child based on id.
Class
public class d3_mitch
{
public int id { get; set; }
public string name { get; set; }
public string type { get; set; }
public string description { get; set; }
public List<d3_mitch> children { get; set; }
}
Object Creation and Query
d3_mitch t = new d3_mitch();
t.id = 1;
t.type = "Root";
t.name = "Animal";
t.description = "A living organism that feeds on organic matter";
t.children = new List<d3_mitch>() {
new d3_mitch() { name = "Carnivores", type = "Type", id = 2, description = "Diet consists solely of animal materials",
children=new List<d3_mitch>(){ new d3_mitch() { id= 3 ,name="Felidae",type="Family",description="Also known as cats"} }
}
};
d3_mitch child = t.children.Where(x => x.id == 3).FirstOrDefault();
//This return null because no direct child has has id = 3 but nested
You need to use recursion. Try next code
d3_mitch FindById(d3_mitch root, int id)
{
if (root.id == id)
return root;
foreach (var child in root.children)
{
if (child.id == id)
return child;
var subTreeResult = FindById(child, id);
if (subTreeResult != null)
return subTreeResult;
}
// no such item
return null;
}
Use SelectMany
t.children.SelectMany(s => s.children)
.FirstOrDefault(s => s.children.Any(d => d.id == 3));
Using a recursive method will resolve your problem.
public static d3_mitch Find(d3_mitch main, int id)
{
if (main.id == id)
return main;
if (main.id != id && main.children != null)
{
foreach (var child in main.children)
{
return child.children.Any(x=>x.id==id)? child.children.First(x=>x.id==id) : Find(child, id);
}
}
return null;
}

Linq query for the Multi list in singe out put

i have table looks like below
ID | Reason | PrID
-----------------
1 abc null
2 dhe null
3 aerc 1
4 dwes 2
5 adfje 1
i have class
public class Reason
{
public int Id { get; set; }
public string Reson{ get; set; }
public List<SecondryReason> SecReason{ get; set; }
public int? PrimaryId { get; set; }
}
public class SecondryReason
{
public int Id { get; set; }
public string Reason { get; set; }
public int PrimaryReasonId { get; set; }
}
I want this to be displayed in hierarchy level
if the prid is Null need to treat this as the parent remaining all child
i am trying Linq and unable to achieve this
Suggest me how to do this in an easy way in linq
So: You have a list/enumerable of type , whereof the SecReason List property is null. Then, using linq you want a list, were the only the "root" reasons remain, but the Sub-reasons got put in the lists, but as type SecondaryReason?
If so, I found this way to do it (linq and foreach):
static IEnumerable<Reason> GetReasonsGrouped(List<Reason> reasons)
{
var result = reasons.Where(x => x.PrimaryId == null);
foreach (var item in result)
{
item.SecReason = reasons.Where(x => x.PrimaryId == item.Id)
.Select(x => new SecondryReason()
{ Id = x.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = item.Id
})
.ToList();
}
return result;
}
Or just linq, but harder to read:
var result = reasons.Where(x => x.PrimaryId == null)
.Select(x =>
{
x.SecReason = reasons.Where(r => x.PrimaryId == x.Id)
.Select(r => new SecondryReason()
{
Id = r.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = x.Id
})
.ToList();
return x;
});
Not sure if linq will be the best solution, here is my proposed changes and method to get an Hierarchy type:
public class Reason
{
public int Id { get; set; }
public string Reson { get; set; }
public List<Reason> SecReason { get; set; }
public int? PrimaryId { get; set; }
//Adds child to this reason object or any of its children/grandchildren/... identified by primaryId
public bool addChild(int primaryId, Reason newChildNode)
{
if (Id.Equals(primaryId))
{
addChild(newChildNode);
return true;
}
else
{
if (SecReason != null)
{
foreach (Reason child in SecReason)
{
if (child.addChild(primaryId, newChildNode))
return true;
}
}
}
return false;
}
public void addChild(Reason child)
{
if (SecReason == null) SecReason = new List<Reason>();
SecReason.Add(child);
}
}
private List<Reason> GetReasonsHierarchy(List<Reason> reasons)
{
List<Reason> reasonsHierarchy = new List<Reason>();
foreach (Reason r in reasons)
{
bool parentFound = false;
if (r.PrimaryId != null)
{
foreach (Reason parent in reasonsHierarchy)
{
parentFound = parent.addChild(r.PrimaryId.Value, r);
if (parentFound) break;
}
}
if (!parentFound) reasonsHierarchy.Add(r);
}
return reasonsHierarchy;
}

What is a cleaner or more efficient way of iterating over nested collections of objects

I'm working on building an object with nested hierarchy from a flattened list of BOM items (bill of materials) from SQL that contain the BOM level and the parent they belong to. Basically each parent that is an assembly of smaller sub parts needs to have its sub parts added to a collection within that object. I believe we have some parts that may iterate up to 10 levels deep, and I don't like the way this pattern is going.
This data set is from a stored procedure that runs a complicated CTE query, and I'm not interested in trying to make this work with Entity Framework, as we have a lot of databases in the mix, and we need better control over the SQL that we're writing (hence the stored procedure). Ultimately this will end up as JSON that I'll render as a collapsible tree in a browser.
Here's the ugly part that I'm hoping to clean up a bit. The gist of what's going on here before I begin iterating over the various levels is:
Establish the first item (incomplete, but this is the main item).
Execute a stored procedure (with parameter) to return a datatable.
Create a flattened list of the objects I want to nest.
Get the total number of nested levels that exist on the BOM.
Iterate over the various levels in a very ugly way.
public BOMItemModel GetItemBOM(string item)
{
try
{
var bom = new BOMItemModel { PLPartNumber = item, BOMLevel = 0 };
var par = new Hashtable();
par.Add("#Item", item);
var dt = db.GetDataTable("[part].[getItemBOM]", par);
var bomList = new List<BOMItemModel>();
foreach(DataRow r in dt.Rows)
{
bomList.Add(MapBomItem(r));
}
var bomLevels = bomList.Max(x => x.BOMLevel);
for(var i = 1; i < bomLevels; i++)
{
foreach (var b in bomList.Where(x => x.BOMLevel == i).ToList())
{
if (i == 1)
{
bom.SubItems.Add(b);
}
else
{
if (i == 2)
{
var lvl2Items = bom.SubItems;
var parent1 = lvl2Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent1 != null) parent1.SubItems.Add(b);
}
if (i == 3)
{
var lvl2Items = bom.SubItems;
foreach(var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
var parent2 = lvl3Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent2 != null) parent2.SubItems.Add(b);
}
}
if (i == 4)
{
var lvl2Items = bom.SubItems;
foreach (var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
foreach (var lvl3 in lvl3Items)
{
var lvl4Items = lvl3.SubItems;
var parent3 = lvl4Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent3 != null) parent3.SubItems.Add(b);
}
}
}
if (i == 5)
{
var lvl2Items = bom.SubItems;
foreach (var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
foreach (var lvl3 in lvl3Items)
{
var lvl4Items = lvl3.SubItems;
foreach (var lvl4 in lvl4Items)
{
var lvl5Items = lvl4.SubItems;
var parent4 = lvl5Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent4 != null) parent4.SubItems.Add(b);
}
}
}
}
if (i == 6)
{
var lvl2Items = bom.SubItems;
foreach (var lvl2 in lvl2Items)
{
var lvl3Items = lvl2.SubItems;
foreach (var lvl3 in lvl3Items)
{
var lvl4Items = lvl3.SubItems;
foreach (var lvl4 in lvl4Items)
{
var lvl5Items = lvl4.SubItems;
foreach (var lvl5 in lvl5Items)
{
var lvl6Items = lvl5.SubItems;
var parent5 = lvl6Items.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber);
if (parent5 != null) parent5.SubItems.Add(b);
}
}
}
}
}
}
}
}
return bom;
}
catch (Exception ex)
{
Log.Error(ex.Message, ex);
}
return null;
}
This is what my model or class looks like:
public class BOMItemModel
{
public BOMItemModel()
{
SubItems = new List<BOMItemModel>();
}
public string ParentPLNumber { get; set; }
public string PartNumber { get; set; }
public string PartListName { get; set; }
public string ItemNumber { get; set; }
public string PLPartNumber { get; set; }
public string PartType { get; set; }
public string PLManufacturer { get; set; }
public string PLDescription { get; set; }
public decimal Qty { get; set; }
public int BOMLevel { get; set; }
public List<BOMItemModel> SubItems { get; set; }
}
The last property on this class is where I'm stuffing sub items of the parent item, and this can nest several levels deep.
Yes, the following would be much better:
for (var i = 1; i <= bomLevels; i++)
{
foreach (var b in bomList.Where(x => x.BOMLevel == i).ToList())
{
if (i == 1)
{
bom.SubItems.Add(b);
}
else
{
var parent = bomList.FirstOrDefault(x => x.PLPartNumber == b.ParentPLNumber && b.BOMLevel - 1 == x.BOMLevel);
if (parent != null) parent.SubItems.Add(b);
}
}
}
Also, I would throw an error when parent is not found. For a more generic approach have a look at my other example: Converting table in tree

Check if two lists are equal with different type

I have a problem with checking for list equality.
I have the following list:
List<RequestDetailViewModel> requestDetail
that RequestDetailViewModel is :
public int PropertiesValueID { get; set; }
public int UnitID { get; set; }
public string Value { get; set; }
public int PropertyID { get; set; }
and i have another list "reqList":
var reqList = (from p in db.RequestDetail
group new
{ p.PropertyID, p.UnitID , p.Value , p.PropertiesValueID }
by p.RequestID into reqG
select reqG
);
i want to check list equality Like this:
foreach (var item in reqList)
{
if (requestDetail equals item)
{
return true;
}
}
How can I solve this?
Modify your reqList select:
var reqList = (from p in db.RequestDetail
group new
{ p.PropertyID, p.UnitID , p.Value , p.PropertiesValueID }
by p.RequestID into reqG
select new RequestDetailViewModel{
PropertyID = reqG.PropertyID, UnitID = reqG.UnitID ,
Value = reqG.Value ,
PropertiesValueID = reqG.PropertiesValueID
});
Will return List<RequestDetailViewModel>
Implement IComparable for your RequestDetailViewModel class then use SequenceEqual to compare two lists
Why don't you loop your View Model list, linq-querying the domain list for each item. Something like:
foreach (var detail in requestDetail)
{
var reqListEqualItems = (from p in db.RequestDetail
where p.PropertyID == details.PropertyID &&
p.UnitID == details.UnitID &&
p.Value == details.Value &&
p.PropertyID == details.PropertyID);
}
Something like that should give you the Domain Object items for each ViewModel item.
I would update the RequestDetailViewModel as follows:
public class RequestDetailViewModel : INotifyPropertyChanged
{
private ResultDetail resultDetail;
public RequestDetailViewModel(ResultDetail resultDetail)
{
this.resultDetail = resultDetail;
}
public ResultDetail
{
get
{
return this.resultDetail;
}
}
public int PropertiesValueID
{
get
{
return this.resultDetail.PropertiesValueID;
}
set
{
this.resultDetail.PropertiesValueID = value;
this.RaisePropertyChanged("PropertiesValueID");
}
}
public int UnitID
{
get
{
return this.resultDetail.UnitID ;
}
set
{
this.resultDetail.UnitID = value;
this.RaisePropertyChanged("UnitID");
}
}
public string Value
{
get
{
return this.resultDetail.Value;
}
set
{
this.resultDetail.Value= value;
this.RaisePropertyChanged("Value");
}
}
public int PropertyID
{
get
{
return this.resultDetail.PropertyID ;
}
set
{
this.resultDetail.PropertyID = value;
this.RaisePropertyChanged("PropertyID");
}
}
}
I'd also implement IEquatable in your DB model.
and I'd do the check like this:
foreach (var item in reqList)
{
if (requestDetail.ResultDetail.Equals(item))
{
return true;
}
}
or better yet
return reqList.Any(item=> item.Equals(requestDetail.ResultDetail));

Refactor query in LINQ

I have a query that needs to check the value from object list if it exists by loop through the value of the main object. How can recode below?
foreach (var kd in dto.KeyDriverModels)
{
var keyDriverModelNodeValue = result.SingleOrDefault(x => x.KeyDriverModelId == kd.ID);
kd.SelectionStatus = keyDriverModelNodeValue != null ?
keyDriverModelNodeValue.SelectionStatus : string.Empty;
}
I tried to model your code and rewrite the code as Linq. See if this works
static DTO dto = new DTO();
static void Main(string[] args)
{
List<KeyDriver> keyDrivers = new List<KeyDriver>();
List<KeyDriver> result = keyDrivers.Where(x => x.KeyDriverModelId > 1000).ToList();
foreach (var kd in dto.KeyDriverModels)
{
var keyDriverModelNodeValue = result.SingleOrDefault(x => x.KeyDriverModelId == kd.ID);
kd.SelectionStatus = keyDriverModelNodeValue != null ?
keyDriverModelNodeValue.SelectionStatus : string.Empty;
}
//new code
var kd1 = from r in result
join d in dto.KeyDriverModels
on r.KeyDriverModelId equals d.ID into drs
from dr in drs
select new { dr }.dr.SelectionStatus != null ? dr.SelectionStatus : string.Empty;
}
}
public class DTO
{
public List<KeyDriverModel> KeyDriverModels { get; set; }
}
public class KeyDriverModel
{
public int ID { get; set; }
public string SelectionStatus { get; set; }
}
public class KeyDriver
{
public int KeyDriverModelId { get; set; }
public string SelectionStatus { get; set; }
}
​

Categories