Filter data from a collection - c#

I have a collection of a Parent class. Parent class has an ID property and some other class property. So, I would like to fetch those child property values on the basis of the Parent ID. I am able to fetch an item of the collection but I need a single value from that item. Below is my code:
public class Parent
{
public int Id { get; set; }
public Child MyChild { get; set; }
}
public class Child
{
public string abc { get; set; }
public string xyz { get; set; }
}
class Program
{
static void Main(string[] args)
{
var d = new List<Parent>();
d.Add(new Parent
{
Id = 1,
MyChild = new Child()
{
xyz = "XYZ one",
abc = "ABC one"
}
});
d.Add(new Parent
{
Id = 2,
MyChild = new Child()
{
xyz = "XYZ two",
abc = "ABC two"
}
});
for (int i = 1; i < 2; i++)
{
var xyzValueOfParentIdOneValue = d.SingleOrDefault(x => x.Id = 1) // Here, I want to get XYZ property value of Parent ID 1.
}
}
}

You could use this
var xyzValueOfParentIdOneValue = d.SingleOrDefault(x => x.Id == 1)
?.MyChild
?.xyz;
if (xyzValueOfParentIdOneValue != null)
{
......
}
Or
var foundItem = d.SingleOrDefault(x => x.Id == 1);
if (foundItem != null && foundItem.MyChild != null)
{
var xyzValueOfParentIdOneValue = foundItem.MyChild.xyz;
}
These two above codes are completely similar.

I think you just want to access the MyChild property of the Parent, like:
var parent = d.SingleOrDefault(x => x.Id == 1);
var xyz = parent.MyChild.xyz;

Since you want to return a default value "0" if the Parent Id doesn't exist, you could use
var xyzValueOfParentIdOneValue = d.SingleOrDefault(x => x.Id == idToSearch)?
.MyChild?
.xyz ?? "0";

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;
}

Best approach to compare if one list is subset of another in C#

I have the below two classes:
public class FirstInner
{
public int Id { get; set; }
public string Type { get; set; }
public string RoleId { get; set; }
}
public class SecondInner
{
public int Id { get; set; }
public string Type { get; set; }
}
Again, there are lists of those types inside the below two classes:
public class FirstOuter
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<FirstInner> Inners { get; set; }
}
public class SecondOuter
{
public int Id { get; set; }
public string Name { get; set; }
public List<SecondInner> Inners { get; set; }
}
Now, I have list of FirstOuter and SecondOuter. I need to check if FirstOuter list is a subset of SecondOuter list.
Please note:
The names of the classes cannot be changed as they are from different systems.
Some additional properties are present in FirstOuter but not in SecondOuter. When comparing subset, we can ignore their presence in SecondOuter.
No.2 is true for FirstInner and SecondInner as well.
List items can be in any order---FirstOuterList[1] could be found in SecondOuterList[3], based on Id, but inside that again need to compare that FirstOuterList[1].FirstInner[3], could be found in SecondOuterList[3].SecondInner[2], based on Id.
I tried Intersect, but that is failing as the property names are mismatching. Another solution I have is doing the crude for each iteration, which I want to avoid.
Should I convert the SecondOuter list to FirstOuter list, ignoring the additional properties?
Basically, here is a test data:
var firstInnerList = new List<FirstInner>();
firstInnerList.Add(new FirstInner
{
Id = 1,
Type = "xx",
RoleId = "5"
});
var secondInnerList = new List<SecondInner>();
secondInner.Add(new SecondInner
{
Id = 1,
Type = "xx"
});
var firstOuter = new FirstOuter
{
Id = 1,
Name = "John",
Title = "Cena",
Inners = firstInnerList
}
var secondOuter = new SecondOuter
{
Id = 1,
Name = "John",
Inners = secondInnerList,
}
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
Need to check if firstOuterList is part of secondOuterList (ignoring the additional properties).
So the foreach way that I have is:
foreach (var item in firstOuterList)
{
var secondItem = secondOuterList.Find(so => so.Id == item.Id);
//if secondItem is null->throw exception
if (item.Name == secondItem.Name)
{
foreach (var firstInnerItem in item.Inners)
{
var secondInnerItem = secondItem.Inners.Find(sI => sI.Id == firstInnerItem.Id);
//if secondInnerItem is null,throw exception
if (firstInnerItem.Type != secondInnerItem.Type)
{
//throw exception
}
}
}
else
{
//throw exception
}
}
//move with normal flow
Please let me know if there is any better approach.
First, do the join of firstOuterList and secondOuterList
bool isSubset = false;
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
var jointOuterList = firstOuterList.Join(
secondOuterList,
p => new { p.Id, p.Name },
m => new { m.Id, m.Name },
(p, m) => new { FOuterList = p, SOuterList = m }
);
if(jointOuterList.Count != firstOuterList.Count)
{
isSubset = false;
return;
}
foreach(var item in jointOuterList)
{
var jointInnerList = item.firstInnerList.Join(
item.firstInnerList,
p => new { p.Id, p.Type },
m => new { m.Id, m.type },
(p, m) => p.Id
);
if(jointInnerList.Count != item.firstInnerList.Count)
{
isSubset = false;
return;
}
}
Note: I am assuming Id is unique in its outer lists. It means there will not be multiple entries with same id in a list. If no, then we need to use group by in above query
I think to break the question down..
We have two sets of Ids, the Inners and the Outers.
We have two instances of those sets, the Firsts and the Seconds.
We want Second's inner Ids to be a subset of First's inner Ids.
We want Second's outer Ids to be a subset of First's outer Ids.
If that's the case, these are a couple of working test cases:
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsTrue(isInnerSubset);
Assert.IsTrue(isOuterSubset);
}
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreNotSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
firstInnerIds.Clear();
firstInnerIds.Add(5);
firstOuterIds.Clear();
firstOuterIds.Add(5);
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsFalse(isInnerSubset);
Assert.IsFalse(isOuterSubset);
}
private List<FirstOuter> GetFirstOuterList() { ... }
private List<SecondOuter> GetSecondOuterList() { ... }

C# Lambda Expressions Using Complex Objects

How can I use a Lambda Expression on a List of Payment objects that have a List PaymentFields object. Here's what I currently have.
var list = paymentList.Where(payment => payment.PaymentFields.Any(field => field.FieldName == "ItemA" && field.FieldValue == "50");
That gives me payments that have ItemA as the Field Name and 50 as the Field Value. However, I want to COMPARE the two PaymentFields like so...
Where FieldName == "ItemA" && FieldValue = "50" && FieldName ItemA < FieldName ItemB
How would I do this?
I have two Objects:
public class Payment
{
public int Id { set; get; }
public string Name { set; get; }
public List<PaymentFields> PaymentFields { set; get; }
}
public class PaymentFields
{
public string FieldName { set; get; }
public string FieldValue { set; get; }
}
Here is an example Object:
var payment = new Payment()
{
Id = 1,
Name = "Test",
PaymentFields = new List<PaymentFields>()
{
new PaymentFields()
{
FieldName = "ItemA",
FieldValue = "20"
},
new PaymentFields()
{
FieldName = "ItemB",
FieldValue = "50"
}
}
};
Thanks for your help!
If you are absolutely sure there will be an "ItemA" and "ItemB", then this will work.
If either "ItemA" or "ItemB" is missing, it will throw an exception.
var list = paymentList
.Where(payment => payment.PaymentFields.Any(field => field.FieldName == "ItemA" && field.FieldValue == "50")
.Where(payment => payment.PaymentFields.First(field => field.FieldName == "ItemA").FieldValue
<
payment.PaymentFields.First(field => field.FieldName == "ItemB").FieldValue)
);

Select an attribute from object in a List inside another List

Can I perform a select using ternary operator to get an attribute from object inside a list?
Here is my model:
public class Xpto
{
public List<Son> Sons { get; set; }
}
public class Son
{
public string Names { get; set; }
}
And here i would like to get "Name" attribute for each son that i have:
var result = (from a in mylist
select new
{
sonsNames = a.Sons == null : <What should i put here?>
}).ToList<object>();
I've tried Sons.ToString() but it prints an object reference.
I would like to have a string list in "sonsNames" and each name separeted by a ','. Example: sonsName: 'george, john'.
what about this ?
//set up a collection
var xptos = new List<Xpto>()
{ new Xpto()
{ Sons = new List<Son>
{ new Son() { Names = "test1" },
new Son() { Names = "test2" }
}
},
new Xpto()
{
Sons = new List<Son> {
new Son() { Names = "test3" }
}
}};
//select the names
var names = xptos.SelectMany(r => r.Sons).Where(k => k.Names != null)
.Select(r => r.Names + ",") .ToList();
names.ForEach(n => Console.WriteLine(n));
Here's more info on SelectMany()

EntityFramework 5 CodeFirst Child Parent of the Same Type Not Updating/Saving

I have a class called Section
public class Section
{
public Section() { construct(0); }
public Section(int order) { construct(order); }
private void construct(int order)
{
Children = new List<Section>();
Fields = new List<XfaField>();
Hint = new Hint();
Order = order;
}
[Key]
public int Id { get; set; }
public int FormId { get; set; }
public string Name { get; set; }
[InverseProperty("Parent")]
public List<Section> Children { get; set; }
public List<XfaField> Fields { get; set; }
public Section Parent { get; set; }
public Hint Hint { get; set; }
public int Order { get; private set; }
#region Methods
public void AddNewChild()
{
AddChild(new Section
{
Name = "New Child Section",
FormId = FormId,
});
}
private void AddChild(Section child)
{
child.Parent = this;
if (Children == null) Children = new List<Section>();
int maxOrder = -1;
if(Children.Count() > 0) maxOrder = Children.Max(x => x.Order);
child.Order = ++maxOrder;
Children.Add(child);
FactoryTools.Factory.PdfSections.Add(child);
}
// Other methods here
#endregion
}
I am trying to add a new child Section to an already existing parent like this:
private void AddChildSection()
{
var parent = FactoryTools.Factory.PdfSections.FirstOrDefault(x => x.Id == ParentId);
if (parent == null) throw new Exception("Unable to create child because parent with Id " + ParentId.ToString() + " doesn't exist.");
parent.AddNewChild();
FactoryTools.Factory.SaveChanges();
}
When I look at the database, I see that a new row has been added, so for example:
Id Name Parent_Id Hint_Id FormId Order
19 New Child Section 1 27 1 0
However, when I load the parent Section, the Children property is always of Count 0, like this:
public ActionResult EditSection(int formId, int sectionId)
{
var model = FactoryTools.Factory.PdfSections.FirstOrDefault(x => x.Id == sectionId);
if (model == null || model.FormId != formId) model = new Section();
//model.Children = FactoryTools.Factory.PdfSections.Where(x => x.Parent.Id == sectionId).ToList();
return PartialView(model);
}
Of course, when I manually add the children, then they are there (in the above code, by uncommenting the model.Children = ... line)
I am used to the NHibernate way of doing things and am therefore quite frustrated that the above, seemingly simple, task is not working in EntityFramework, what am I doing wrong?
Entity Framework won't eagerly load related entities. Try forcing it to include the children:
var model = FactoryTools.Factory.PdfSections.Include("Children").FirstOrDefault(x => x.Id == sectionId);
There's also a strongly-typed overload to which you can pass a lambda:
var model = FactoryTools.Factory.PdfSections.Include(s => s.Children).FirstOrDefault(x => x.Id == sectionId);

Categories