Linq query to get shared items in a sublist - c#

I have a class that has a property which is a List, I will name this class A. Then I have a List<A>.
I need a LINQ for objects to get all the objects B that are present on ALL the items on the List<A>.
Example to clarify:
var list = new List<A>
{
new A { B = new List<B> { B1, B2, B3, B4 } }
new A { B = new List<B> { B3, B4, B5, B6 } }
new A { B = new List<B> { B2, B3, B4, B5, B6 } }
};
The query must return the objects B3 and B4 because are the only ones contained on all the List<A> objects.

If you have a list of lists and you want the elements that are in all the inner lists, you can use a combination of Aggregate and Intersect, like this:
IEnumerable<IEnumerable<string>> listOfLists = new string[][] {
new string[] { "B1", "B2", "B3", "B4" },
new string[] { "B3", "B4", "B5", "B6" },
new string[] { "B2", "B3", "B4", "B5", "B6" }
};
IEnumerable<string> commonElements = listOfLists.Aggregate(Enumerable.Intersect);

You can just use Intersect() provided there is at least one element and your class B has an adequate implementation of equality / GetHashCode():
IEnumerable<B> allBs = list[0].B;
foreach (var item in list.Skip(1))
{
allBs = allBs.Intersect(item.B);
}
I see no benefit of a "pure" Linq solution here besides adding complexity.

As long as the Equals/GetHashCode are properly defined for objects of type B, then this is actually somewhat simple:
_listToQuery.Aggregate(
_listToQuery.First().B,
(seed, nextItem) => { seed = seed.Intersect(nextItem.B); return seed; })
Or, another way:
_listToQuery.SelectMany(tr => tr.B)
.GroupBy(tr => tr)
.Where(tr => tr.Count() == _listToQuery.Count)
.Select(tr => tr.Key)

The way I would approach this is to create a list of all the B's then find out which ones occur more than once by grouping them by some unique identifier (or just the object if they are comparable). Once they are grouped, select those where the number of instances is greater than 1, using the first instance of each grouping as the canonical representative.
var selection = list.SelectMany( a => a.B )
.GroupBy( b => b.UniqueID, b => b )
.Where( b => b.Count() > 1 )
.Select( b => b.First() );

Related

C# Linq map one list with another list in the same order

I am very new to LINQ
in Linq below is what I am trying to achieve :
I have two classes -
List<ClassA> - List<ClassB>
Now I want to map 1st item of Class A to first item of Class b
ClassA.ForEach( a => {
// how to get the items from ClassB in the same order as it is in CLass b and assign it to Class A
});
Are you looking for Zip? If you want to enumerate pairs (1st item from listA, 1st item from listB; 2nd item from listA, 2nd item from listB ... nth item from listA, nth item from listB):
// a and b are corresponding items from listA and listB
foreach (var (a, b) in listA.Zip(listB, (a, b) => (a, b))) {
//TODO: relevant code here; a is from listA, b is from listB
}
For instance:
List<int> listA = new List<int>() {1, 2, 3};
List<string> listB = new List<string>() {"A", "B", "C"};
foreach (var (a, b) in listA.Zip(listB, (a, b) => (a, b)))
{
Console.WriteLine($"{a} : {b}");
}
Output:
1 : A
2 : B
3 : C
Please, fiddle yourself
i would try something like this:
List<int> listA = new List<int>();
List<int> listB = new List<int>();
listA.Add(1);
listB.AddRange(listA);
i know it is not LINQ but i think this approach is simpler
You can Select the item along with its index as follows:
yourList.Select((x, i) => new {element = v, index = i})
Then, you can apply First to join them by index value:
tempList = list2.Select((x, i) => new { somethingElse = x, index = i });
list1.Select((x, i) => new {
something = x,
somethingElse => tempList.First((item) => item.index == i) )
});
I do not have a .NET env at hand and I did not work in it for a long while, so if there are typos, let me know.

Is there one liner LINQ construct for creating dictionary by merging two lists and removing duplicates

I have two classes, and two collections of each:
public class ClassA {
String key;
String value;
// Some other fields specific to ClassA
}
public class ClassB {
String key;
String value;
// Some other fields specific to ClassB
}
List<ClassA> listA;
List<ClassB> listB;
I want to build Dictionary<String, String> from key and value from listA and listB where value would be from listB if both lists contain object with same key but different values.
Now, I have a solution - create dictionaryA from listA, create dictionaryB from listB, merge those dictionaries by properly handling duplicates. There are several SO posts explaining how to do just that.
My question is bit more academic - Is there one liner LINQ construct that can do what I want to do?
Here is one way:
var merged =
listA.Select(b => new { key = b.key, val = b.value })
.Union(listB.Select(b => new { key = b.key, val = b.value }))
.ToDictionary(m => m.key, n => n.val);;
Please note that this will not handle objects that have the same key but different value.
To deal with duplicates you'd need:
var d = listA.Select(b => new { key = b.key, val = b.value })
.Union(listB.Select(b => new { key = b.key, val = b.value }))
.GroupBy(x => x.key)
.Select(x => x.First())
.ToDictionary(m => m.key, n => n.val);
Please note this keeps only the first record with a given key and records with the same key, but different value are lost.
Test code
public static void Main(string[] args)
{
List<ClassA> listA = new List<ClassA>() {
new ClassA() { key = "A", value = "1" },
new ClassA() { key = "B", value = "2" }};
List<ClassB> listB = new List<ClassB>() {
new ClassB() { key = "B", value = "2" },
new ClassB() { key = "C", value = "3" },
new ClassB() { key = "A", value = "4" }};
var d = (...)
foreach( var kvp in d ) {
Console.WriteLine($"{kvp.Key}: {kvp.Value} ");
}
}
Result
A: 1
B: 2
C: 3

LINQ group items into lists in anonymous type, with duplicates

I have 3 boxes that can contain fruit:
A - apples, oranges, pears
B - apples, bananas
C - pears
I'd like to create a LINQ query statement that generates a new anonymous type that groups the boxes by the fruit they contain (not actual code):
fruitBoxes.apples = {A, B}
fruitBoxes.oranges = {A}
fruitBoxes.bananas = {B}
fruitBoxes.pears = {A, C}
All anonymous type properties have to be known at compile time, so unless you know exactly what fruits you're going to deal with (which is unlikely) you can't use anonymous types.
You can use Dictionary<string, List<string>> instead:
var result = boxes.SelectMany(b => b.Fruits.Select(f => new { Box = b, Fruit = f }))
.GroupBy(x => x.Fruit, x => x.Box.Name)
.ToDictionary(g => g.Key, g => g.ToList());
Box is defined as:
class Box
{
public string Name { get; set; }
public List<string> Fruits { get; set; }
}
You could do this:
var boxes = new []
{
new { box = "A", fruit = new [] { "apples", "oranges", "pears", }, },
new { box = "B", fruit = new [] { "apples", "bananas", }, },
new { box = "C", fruit = new [] { "pears", }, },
};
var query =
from b in boxes
from f in b.fruit
group b.box by f into bs
select new
{
fruit = bs.Key,
boxes = bs.ToArray(),
};
The result I get it this:

Finding all combinations from sets of possibilities

I have multiple sets of arrays that contain additional arrays that have values attached that I use for figuring out math. In order to find the best combination of these things, I need to mix and match from these arrays. I've seen "solutions" similar to this around, but they're usually 1 array deep with no real combinations/possibilities. So to give an example.
I have sets A, B, and C. Set A contains Aa, Ab, Ac, and Ad. Aa contains a set of values. Extrapolate that out for the others. Aa can only be compared with Ba and Ca. How do I go about writing a program to find all combinations(i.e. Aa, Ab, Cc, Bd compared with Ba, Cb, Ac, Bd and etc) so I can compare the math on each combination to find the best one? Note: this is just an example, I don't need it for specifically 3 sets of 4 sets of 4, it needs to be able to expand.
Now I know I didn't use very meaningful names for my variables, but I would appreciate if any code given does have meaningful names in it(I'd really rather not follow around variables of x and c around in code).
The accepted answer appears to be correct but is a very strange way to do a Cartesian product in C#. If you have a given number of sequences you can take their Cartesian product idiomatically like this:
var aList = new[] { "a1", "a2", "a3" };
var bList = new[] { "b1", "b2", "b3" };
var cList = new[] { "c1", "c2", "c3" };
var product = from a in aList
from b in bList
from c in cList
select new[] { a, b, c };
foreach (var p in product)
Console.WriteLine(string.Join(",", p));
If you have arbitrarily many sequences that you need to take their Cartesian product then you can do it like this:
static class Extensions
{
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item}));
}
}
And then:
var aList = new[] { "a1", "a2", "a3" };
var bList = new[] { "b1", "b2", "b3" };
var cList = new[] { "c1", "c2", "c3" };
var lists = new[] { aList, bList, cList };
var product = lists.CartesianProduct();
foreach (var p in product)
Console.WriteLine(string.Join(",", p));
See
http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
and my answer to
Generating all Possible Combinations
for more discussion of this problem.
Assuming you are using a version of C# which supports LINQ:
static void Main(string[] args)
{
// declare some lists
var aList = new string[] { "a1", "a2", "a3" };
var bList = new string[] { "b1", "b2", "b3" };
var cList = new string[] { "c1", "c2", "c3" };
// do the equivalent of a SQL CROSS JOIN
var permutations = aList
.Join(bList, a => "", b => "", (a, b) => new string[] { a, b })
.Join(cList, ab => "", c => "", (ab, c) => new string[] { ab[0], ab[1], c });
// print the results
Console.WriteLine("Permutations:");
foreach (var p in permutations)
Console.WriteLine(string.Join(", ", p));
}
The Join calls with the lambda expressions pointing the strings to empty strings causes the Join function to treat the strings as equal, emulating a SQL CROSS JOIN.

C# & Linq: Grouping list members according to a list of values

I have a list of object which all expose a property of type IList.
Now I want to group this list by the values of that list. So let say for example:
OB1: Property is A, B, C
OB2: Property is D, C, E
OB3: Property is B, E, C
As output I would like to have
A: OB1
B: OB1, OB3
C: OB1, OB2, OB3
D: OB2
E: OB2, OB3
I thought about a convenient LINQ expression to solve this, but it could not find any reference if it is possible at all. Of cause I can to it with loops... but I'am curious if it is possible with LINQ.
Thanks
Sample for LINQPad:
var original = new[]
{
new { Name = "OB1", Property = new [] { "A", "B", "C" } },
new { Name = "OB2", Property = new [] { "D", "C", "E" } },
new { Name = "OB3", Property = new [] { "B", "E", "C" } },
};
var output = original
.SelectMany(o => o.Property, (o, i) => new { Name = o.Name, Item = i })
.GroupBy(e => e.Item);
Assuming a structure like this:
var list = new [] {
new {Name="OB1", Prop=new[]{"A", "B", "C"}},
new {Name="OB2", Prop=new[]{"D", "C", "E"}},
new {Name="OB3", Prop=new[]{"B", "E", "C"}},
}
You can write the following query comprehension:
from ob in list
let Name = ob.Name
from val in ob.Props
group ob.Name by val
If you want to map directly to objects, not just their names, do this instead:
from ob in list
from val in ob.Props
group ob by val
You can try:
list
.SelectMany(x => x.Property.Select(p => new { Key = p, Value = x }))
.GroupBy(p => p.Key)
.Select(g => new { g.Key, Values = g.Select(x => x.Value).ToList() } )
var list = new[] {
new {Name="OB1", Prop=new[]{"A", "B", "C"}},
new {Name="OB2", Prop=new[]{"D", "C", "E"}},
new {Name="OB3", Prop=new[]{"B", "E", "C"}},
};
var props = from prop in (from item in list
from p in item.Prop
select p).Distinct()
let names = list.Where(i => i.Prop.Contains(prop)).Select(i => i.Name).ToArray()
select new { prop, names };

Categories