Select an attribute from object in a List inside another List - c#

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

Related

Compare 2 lists of same object type

I have 2 lists of a specific type, in this case it is List. In the class DataDictionary there is a property called TableName. I have 2 lists with the same type I am trying to compare. I have other properties aswell which I need to hold association with that specific TableName so I can't just compare them separately.
I need to find a way to compare the TableName in 2 different lists of DataDictionary and then find which ones they don't have in common. From there I then need to compare all the other properties against the 2 items in each list with the same TableName.
I have tried to use the Except IEnumerate solution which works if you just compare the strings directly but I don't know how to keep the association with the object.
List<DataDictionary> ColumnsDataDict = daDD.getTablesandColumnsDataDictionary();
List<DataDictionary> ColumnsWizard = daWiz.getColumnsWizard();
var newlist = ColumnsWizard.Except(ColumnsDataDict);
foreach(DataDictionary item in newlist)
{
Console.WriteLine(item.TableName);
}
Here is the DataDictionary class:
public string TableName { get; set; }
public string Description { get; set; }
public string TableID { get; set; }
public string ColumnDesc { get; set; }
public string ColumnName { get; set; }
This directly compares the objects, but I just want to compare the TableName property in my DataDictionary class. I want this to then get a list of objects that doesn't have the same table name in each list. Any help is appreciated, thanks!
I don't believe your problem can be solved with LINQ alone and there for I would recommend using a good old loop.
If you want to compare two lists, you will have to use two loops nested in one another.
Like this:
public class Program
{
public static void Main()
{
var listA = new List<Foo>() {
new Foo() { TableName = "Table A", Value = "Foo 1" },
new Foo() { TableName = "Table B", Value = "Foo 1" },
};
var listB = new List<Foo>() {
new Foo() { TableName = "Table A", Value = "Foo 10" },
new Foo() { TableName = "Table C", Value = "Foo 12" },
};
foreach (var itemA in listA)
{
foreach (var itemB in listB)
{
if (itemA.TableName == itemB.TableName)
{
Console.WriteLine($"ItemA's Value: {itemA.Value}");
Console.WriteLine($"ItemB's Value: {itemB.Value}");
}
}
}
}
}
public class Foo
{
public string TableName { get; set; }
public string Value { get; set; }
}
At the position where I am printing the values of itemA and itemB you can compare your objects and find the difference between them.
So instead of:
Console.WriteLine($"ItemA's Value: {itemA.Value}");
Console.WriteLine($"ItemB's Value: {itemB.Value}");
Maybe something like this:
if (itemA.Value != itemB.Value) //extend the `if` with all the properties you want to compare
{
Console.WriteLine($"ItemA's Value isn't equal to ItemB's Value");
}
If you need to check if listA doesn't have an entry in listB then you can extend the inner loop like this:
foreach (var itemA in listA)
{
var found = false;
foreach (var itemB in listB)
{
if (itemA.TableName == itemB.TableName)
{
found = true;
}
}
if (found == false)
{
Console.WriteLine("ItemA's TableName wasn't found in listB");
}
}
SOLUTION:
// Merges both objects
List<DataDictionary> duplicatesRemovedLists = ColumnsDataDict.Concat (ColumnsWizard).ToList ();
// Removes common objects based on its property value (eg: TableName)
foreach (var cddProp in ColumnsDataDict) {
foreach (var cwProp in ColumnsWizard) {
if ((cddProp.TableName == cwProp.TableName)) {
duplicatesRemovedLists.Remove (cddProp);
duplicatesRemovedLists.Remove (cwProp);
}
}
}
// Prints expected output
foreach (DataDictionary item in duplicatesRemovedLists) Console.WriteLine (item.TableName);
You can use several LINQ methods implementing a customer IEqualityComparer.
For instance having a comparer for the property TableName:
public class TableNameEqualityComparer : IEqualityComparer<DataDictionary>
{
public bool Equals(DataDictionary x, DataDictionary y)
{
if (x == null && y == null)
{
return true;
}
return x != null && y != null && x.TableName == y.TableName;
}
public int GetHashCode(DataDictionary obj)
{
return obj?.TableName?.GetHashCode() ?? 0;
}
}
Two instances returning true for the Equals method must return
the same value for GetHashCode.
Just make an instance of your custom comparer.
var listA = new List<DataDictionary>()
{
new DataDictionary() {TableName = "Table A"},
new DataDictionary() {TableName = "Table B"},
};
var listB = new List<DataDictionary>()
{
new DataDictionary() {TableName = "Table A"},
new DataDictionary() {TableName = "Table C"},
};
var tableNameComparer = new TableNameEqualityComparer();
And then use it with different LINQ methods:
// A - B
var listAExceptB = listA.Except(listB, tableNameComparer);
Assert.Collection(listAExceptB,
x => Assert.Equal("Table B", x.TableName));
// B - A
var listBExceptA = listB.Except(listA, tableNameComparer);
Assert.Collection(listBExceptA,
x => Assert.Equal("Table C", x.TableName));
// A ∩ B
var listIntersect = listA.Intersect(listB, tableNameComparer);
Assert.Collection(listIntersect,
x => Assert.Equal("Table A", x.TableName));
// A ∪ B
var listUnion = listA.Union(listB, tableNameComparer);
Assert.Collection(listUnion,
x => Assert.Equal("Table A", x.TableName),
x => Assert.Equal("Table B", x.TableName),
x => Assert.Equal("Table C", x.TableName));

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# Merge List of sub objects from parent level Object List

If you had a List and wanted to merge the sub List for any SomeObject's that have the same Id field how would you do that? Here are the example objects:
public class SomeObject
{
public string Name { get; set; }
public int Id { get; set; }
public List<KeyPair> ValuePairs {get;set;}
}
public class KeyPair
{
public string Key { get; set; }
public string Value { get; set; }
}
And this is the sample creation of a mock list:
List<SomeObject> objects = new List<SomeObject>();
objects = new List<SomeObject>()
{
new SomeObject
{
Name="Rando Object 1",
Id=5,
ValuePairs=new List<KeyPair>()
{
new KeyPair
{
Key="TestKey1",
Value="TestValue1"
},
new KeyPair
{
Key="TestKey2",
Value="TestValue2"
}
}
},
new SomeObject
{
Name="Rando Object 2",
Id=5,
ValuePairs=new List<KeyPair>()
{
new KeyPair
{
Key="TestKey3",
Value="TestValue3"
},
new KeyPair
{
Key="TestKey4",
Value="TestValue4"
}
}
}
};
What sort of Linq or related query would you need to do to create a new list of SomeObject that is merged based on any top level SomeObject's that have matching Id fields; to then combine their KeyPair list to a single list. So you would have SomeObject Id=5 and then 4 key pair values merged from the two different previous SomeObject's in the list. The name value could be left out from the new object.
Any ideas? Thank you so much.
You need to group them by Id and use SelectMany to select KeyPair list.
var result = objects.GroupBy(o => o.Id).Select(group => new SomeObject
{
Id = group.Key,
ValuePairs = group.SelectMany(x => x.ValuePairs).ToList()
}).ToList();
You can try this:
var res = objects.GroupBy(o => o.Id)
.Select(group => new {
Id = group.Key,
ValuePairs = group.SelectMany(g => g.ValuePairs)
});
Original post:
var res = objects.Where(o => o.Id == 5).SelectMany(o => o.ValuePairs);
Use this function
https://dotnetfiddle.net/aE6p5H
public List<SomeObject> MergeObj(List<SomeObject> someObjects)
{
var idList = someObjects.Select(x => x.Id).Distinct().ToList();
var newSomeObjects = new List<SomeObject>();
idList.ForEach(x =>
{
var newValuePairList = new List<KeyPair>();
someObjects.Where(y => y.Id == x).ToList().ForEach(y =>
{
newValuePairList.AddRange(y.ValuePairs);
});
newSomeObjects.Add(new SomeObject{Id = x, ValuePairs = newValuePairList});
});
return newSomeObjects;
}

linq lambda convert object with list of objects of the same type to another object

I currently have a List<OriginalItem> that i need to convert to a List<NewItem>.
Here are the classes
public class OriginalItem
{
public int ItemIndex {get;set;}
public string Name {get;set;}
private OriginalItem[] itemsList;
public OriginalItem[] ItemsList
{
get
{
return itemsList;
}
set
{
itemsList= value;
}
}
}
public class NewItem
{
public int NewItemIndex {get;set;}
public string NewName {get;set;}
private NewItem[] itemsList;
public NewItem[] ItemsList
{
get
{
return itemsList;
}
set
{
itemsList= value;
}
}
}
I know using a select statement i can create a new object from a list i.e.
List<NewItem> newItems = originalItems.Select(x=> new NewItem(){
NewItemIndex = x.ItemIndex,
NewItemName = x.Name
}).ToList();
but how do i create the list within the list? It doesnt have to use recursion, if there is another way to do it.
Thanks,
from the above i managed to get what i need with minor changes:
public class OriginalItem
{
.
.
.
//add new method to convert the originalItem to newItem
public NewItem createNewItem()
{
NewItem item = new NewItem();
item.NewName = this.Name;
item.NewItemIndex = this.ItemIndex;
item.ItemsList = this.ItemsList.Select(x =>x.createNewItem()).ToList();
}
}
and then in the main class where i had the List<OriginalItem> originalItems I did the following:
List<NewItem> newItems = originalItems.Select(x=>x.createNewItem()).ToList();
You can't get by without a recursion with recursive properties. This operation is essentially recursive.
One possible implementation:
NewItem ToNew(OriginalItem item)
{
return new NewItem() { NewItemIndex = item.ItemIndex, NewName = item.Name, ItemsList = item.ItemsList == null ? null : item.ItemsList.Select(ToNew).ToArray()};
}
And here is how to use it:
var newItems = originalItems.Select(ToNew).ToList();
It also could be implemented as a copy constructor, but no reason to cople the two classes.
Also it's more convenient to use as a method group (just ToNew, not i => ToNew(i)).
Here's how I would do it:
Func<OriginalItem, NewItem> convert = null;
convert = oi =>
new NewItem()
{
NewName = oi.Name,
NewItemIndex = oi.ItemIndex,
ItemsList = oi.ItemsList == null
? null
: oi.ItemsList.Select(x => convert(x)).ToArray(),
};
var newItems = originalItems.Select(oi => convert(oi)).ToList();
So, given this input:
var originalItems = new List<OriginalItem>()
{
new OriginalItem()
{
Name = "0", ItemIndex = 0,
ItemsList = new []
{
new OriginalItem()
{
Name = "1", ItemIndex = 1, ItemsList = null,
},
new OriginalItem()
{
Name = "2", ItemIndex = 2, ItemsList = null,
},
},
},
new OriginalItem()
{
Name = "3", ItemIndex = 3, ItemsList = null,
},
};
I get this output:

How to make select query by string property name in lambda expression?

I would like to make a query by using lambda select,
Like below:
public class Foo{
public int Id {get;set;}
public string Name {get;set;}
public string Surname {get;set;}
}
var list = new List<Foo>();
var temp = list.Select(x=> x("Name"),("Surname"));
The property name needs to be sent as a string,
I dont know how to use, I have given it for being a example.
is it possible?
Edit:
Foo list :
1 A B
2 C D
3 E F
4 G H
I don't know type of generic list, I have property name such as "Name", "Surname"
I want to be like below:
Result :
A B
C D
E F
G H
The following code snippet shows 2 cases. One filtering on the list, and another creating a new list of anonymous objects, having just Name and Surname.
List<Foo> list = new List<Foo>();
var newList = list.Select(x=> new {
AnyName1 = x.Name,
AnyName2 = x.Surname
});
var filteredList = list.Select(x => x.Name == "FilteredName" && x.Surname == "FilteredSurname");
var filteredListByLinq = from cust in list
where cust.Name == "Name" && cust.Surname == "Surname"
select cust;
var filteredByUsingReflection = list.Select(c => c.GetType().GetProperty("Name").GetValue(c, null));
Interface
If you have access to the types in question, and if you always want to access the same properties, the best option is to make the types implement the same interface:
public interface INamable
{
string Name { get; }
string Surname { get; }
}
public class Foo : INamable
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
This will preserve type safety and enable queries like this one:
public void ExtractUsingInterface<T>(IEnumerable<T> list) where T : INamable
{
var names = list.Select(o => new { Name = o.Name, Surname = o.Surname });
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
If, for some reason, you can't alter the original type, here are two more options.
Reflection
The first one is reflection. This is Mez's answer, i'll just rephrase it with an anonymous type like in the previous solution (not sure what you need exactly):
public void ExtractUsingReflection<T>(IEnumerable<T> list)
{
var names = list.Select(o => new
{
Name = GetStringValue(o, "Name"),
Surname = GetStringValue(o, "Surname")
});
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
private static string GetStringValue<T>(T obj, string propName)
{
return obj.GetType().GetProperty(propName).GetValue(obj, null) as string;
}
Dynamic
The second uses dynamic:
public void ExtractUsingDynamic(IEnumerable list)
{
var dynamicList = list.Cast<dynamic>();
var names = dynamicList.Select(d => new
{
Name = d.Name,
Surname = d.Surname
});
foreach (var n in names)
{
Console.WriteLine(n.Name + " " + n.Surname);
}
}
With that in place, the following code:
IEnumerable<INamable> list = new List<Foo>
{
new Foo() {Id = 1, Name = "FooName1", Surname = "FooSurname1"},
new Foo() {Id = 2, Name = "FooName2", Surname = "FooSurname2"}
};
ExtractUsingInterface(list);
// IEnumerable<object> list... will be fine for both solutions below
ExtractUsingReflection(list);
ExtractUsingDynamic(list);
will produce the expected output:
FooName1 FooSurname1
FooName2 FooSurname2
FooName1 FooSurname1
FooName2 FooSurname2
FooName1 FooSurname1
FooName2 FooSurname2
I'm sure you can fiddle with that and get to what you are trying to achieve.
var temp = list.Select(x => x.Name == "Name" && x.Surname == "Surname");
var temp = list.Select(x => new {Name = x.Name, Surname = x.Surname});

Categories