Linq Join: Join using a mapping table and associate objects - c#

I am trying to write a linq query to make a very basic join. I have two arrays
Park[] parks = new Park[]{
new Park() {ID = 1, Name = "Free Park"},
new Park() {ID = 2, Name = "Cost Park"},
new Park() {ID = 3, Name="Sneak in Park"}
};
and
Facility[] facilities = new Facility[] {
new Facility() { ID = 1, Name = "Swing", MinimumAge = 1, MaximumAge = 120},
new Facility() { ID = 2, Name = "Slide", MinimumAge = 1, MaximumAge = 200},
new Facility() { ID = 3, Name = "See-Saw", MinimumAge = 1, MaximumAge = 300}
};
Each park can have 0...n facilities, hence we have a set of mapping objects
ParkFacility[] associations = new ParkFacility[] {
new ParkFacility() {ParkID = 1, FacilityID = 1},
new ParkFacility() {ParkID = 1, FacilityID = 2},
new ParkFacility() {ParkID = 1, FacilityID = 3},
new ParkFacility() {ParkID = 2, FacilityID = 1},
new ParkFacility() {ParkID = 3, FacilityID = 2}
};
This is the definition of the class Park
class Park
{
public int ID { get; set; }
public String Name { get; set; }
public Facility[] Facilities { get; set; }
}
Is it possible to use only joins and associate the appropriate facilities to the parks? i.e. set the Facilities array in Park to be those appropriately mapped using the associations?
Edit: My research thus far..
var x_temp = from g in parks
join j in associations on g.ID equals j.ParkID into h
select new Park()
{
Name = g.Name,
ID = g.ID,
Facilities = (from u in h join m in facilities on u.FacilityID equals m.ID select m).ToArray()
};
I tried using a sub-linq query and it works, but I am looking for a solution with only joins

You can create a lookup from park id to facilities, and use this to populate the Facilities property on each of your Park objects. Note that the best place to do this is in the constructor of Park, but in keeping with your existing code this snippet will do it in the object initializer:
var lookup = associations.ToLookup(pf => pf.ParkID, pf => facilities.Single(f => f.ID == pf.FacilityID));
Park[] parks = new Park[]{
new Park() {ID = 1, Name = "Free Park", Facilities = lookup[1].ToArray()},
new Park() {ID = 2, Name = "Cost Park", Facilities = lookup[2].ToArray()},
new Park() {ID = 3, Name="Sneak in Park", Facilities = lookup[3].ToArray()}
};
Additionally, it would be helpful to store all your Facility and Park instances in a Dictionary mapping the ID to the instance. In that case your lookup wouldn't need to do a linear scan through the all the facilities for each association.

Related

Linq how to find max repeat items?

I have a list of object (ProductInfo).
ProductInfo contains an id, name, and an option.
Imagine this sample, i have this
ProductInfo Id => 1, Name => XXX, Option = A
ProductInfo Id => 1, Name => XXX, Option = B
ProductInfo Id => 2, Name => DEB, Option = A
ProductInfo Id => 2, Name => DEB, Option = B
ProductInfo Id => 2, Name => DEB, Option = C
ProductInfo Id => 3, Name => ZZZ, Option = D
....
....
We see we have 2 time the option A AND B for product 1 and 2.
My goal will be to obtain the max repeat item for each product in the list.
i would like to obtain as result this :
Id = 1, Name = XXX = A, count = 2
Id =2, Name = DEB, count = 2
How i can do that ?
thanks for your time
try to do this code:
var list = new List<ProductInfo> {
new ProductInfo { Id = 1, Name = "XXX", Option = "A"},
new ProductInfo { Id = 1, Name = "XXX", Option = "B" },
new ProductInfo { Id = 2, Name = "DEB", Option = "A" },
new ProductInfo { Id = 2, Name = "DEB", Option = "B"},
new ProductInfo { Id = 2, Name = "DEB", Option = "C" },
new ProductInfo { Id = 3, Name = "ZZZ", Option = "D" }
};
var x = from p in list
group p by new { p.Id, p.Name, p.Option } into g
select new
{
Id = g.Key.Id,
Name = g.Key.Name,
Count = list.Count(m => m.Name == g.Key.Name)
};
var t = x.Distinct();
You can use GroupBy on the Name and Id parameter. Sorry read that wrong at first.

How to split array to many arrays where id same linq

I have array:
OrderProduct[] OrderProductsOrder = new OrderProduct[] {
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 1 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 1 },
new OrderProduct { OrderID = 1, ProductID = 3, OrderCustomerID = 1 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 2 },
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 3 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 3 }};
How to split this array to three arrays, order by CustomerID, using linq.
Result should be this three arrays:
OrderProduct[] Customer1Order = new OrderProduct[] {
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 1 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 1 },
new OrderProduct { OrderID = 1, ProductID = 3, OrderCustomerID = 1 }};
OrderProduct[] Customer2Order = new OrderProduct[]
{new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 2 }};
OrderProduct[] Customer3Order = new OrderProduct[] {
new OrderProduct { OrderID = 1, ProductID = 2, OrderCustomerID = 3 },
new OrderProduct { OrderID = 2, ProductID = 1, OrderCustomerID = 3 }};
Edited, removed the GroupBy() suggestion as it was redundant (courtesy of Innat3)
No reason to use GroupBy() at all, just use Where.
OrderProduct[] Customer1Order = OrderProductsOrder.Where(o => o.OrderCustomerID == 1).ToArray();
OrderProduct[] Customer2Order = OrderProductsOrder.Where(o => o.OrderCustomerID == 2).ToArray();
OrderProduct[] Customer3Order = OrderProductsOrder.Where(o => o.OrderCustomerID == 3).ToArray();
Start by grouping the entries by OrderCustomerID, and constructing an array from each group. After that, add groups to a dictionary:
var byCustId = OrderProductsOrder
.GroupBy(p => p.OrderCustomerID)
.ToDictionary(g => g.Key, g => g.ToArray());
Now you can grab individual arrays with TryGetValue or operator []:
OrderProduct[] customer2Order;
if (byCustId.TryGetValue(2, out customer2Order) {
... // Use customer2Order array
}

How to merge 2 listitems

I have 2 listitems
First
{Id = 1, Name = "a"}{Id = 2, Name = "b"}{Id = 3, Name = "c"}
Second
{Id = 2, Name = "b"}{Id = 3, Name = "c"}{Id = 4, Name = "d"}
how to merge them and get a listitem like:
{Id = 1, Name = "a"}{Id = 2, Name = "b"}{Id = 3, Name = "c"}{Id = 4, Name = "d"}
Something like this ?
class items {
public int id;
public string name;
}
static void Main(string[] args)
{
var list = new List<items>();
list.Add(new items {id=1 , name="a"});
list.Add(new items { id = 2, name = "b" });
list.Add(new items { id = 3, name = "c" });
var list1 = new List<items>();
list1.Add(new items { id = 2, name = "b" });
list1.Add(new items { id = 3, name = "c" });
list1.Add(new items { id = 4, name = "d" });
var c = list.Select(b=> new {b.id, b.name});
var d = list1.Select(b => new { b.id, b.name });
var merge = c.Concat(d).Distinct().ToList();
foreach (var item in merge)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
var list1 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
var list2 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
list1.AddRange(list2);
Then list1 is the merged list.
You can use the "AddRange" or "Union" or "Concat" functions of c#
e.g
var list1 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
var list2 = new[]{new {Id = 1, Name = "a"},new {Id = 2, Name = "b"}, new{Id = 3, Name = "c"}}.ToList();
then
var MergedResult = list1.Union(list2).ToList();
or
var MergedResult = list1.AddRange(list2).ToList();
also you can use Concat as well...
see the difference amongst them here .NET List Concat vs AddRange

Sorting an IEnumerable in LINQ

How to sort the given examples.
IEnumerable<extra> eList = new List<extra>()
{
new extra{ id = 1, text = "a"},
new extra{ id = 2, text = "g"},
new extra{ id = 3, text = "i"},
new extra{ id = 4, text = "e"},
new extra{ id = 5, text = "f"},
new extra{ id = 6, text = "d"},
new extra{ id = 7, text = "c"},
new extra{ id = 8, text = "h"},
new extra{ id = 9, text = "b"}
};
IEnumerable<sample> sam = new List<sample>()
{
new sample{ id = 1, name = "sample 1", list = new List<int>{1,5,6}},
new sample{ id = 2, name = "sample 1", list = new List<int>{2,9}},
new sample{ id = 3, name = "sample 1", list = new List<int>{8,3,7}},
new sample{ id = 4, name = "sample 1", list = new List<int>{3,4,8}},
new sample{ id = 5, name = "sample 1", list = new List<int>{1,5,7}},
new sample{ id = 6, name = "sample 1", list = new List<int>{6,9,7}}
};
I have this code to sort and join the sample list to the extra object above.
var s2 = (from d1 in sam
select new
{
name = d1.name,
id = d1.id,
list =
(
from d2 in d1.list
join e in eList on d2 equals e.id
select new {
id = d2, text = e.text
}
).OrderBy(item => item.text.FirstOrDefault())
});
The code above works fine, it joined the two data and sorted the values for the list. But what I want is the output above 's2' will be sorted again by its 'list' value by 'list.text'.
So possible output above must be:
{ id = 1, name = "sample 1", list = {'a','f','d'}},
{ id = 5, name = "sample 1", list = {'a','f','c'}},
{ id = 2, name = "sample 1", list = {'g','b'}},
{ id = 4, name = "sample 1", list = {'i','e','h'}},
{ id = 6, name = "sample 1", list = {'d','b','c'}},
{ id = 3, name = "sample 1", list = {'h','i','c'}},
Is this possible in LINQ?
thanks
var newsam = sam.Select(s => new
{
id = s.id,
name = s.name,
list = s.list
.Select(l => eList.FirstOrDefault(e => e.id == l).text)
.OrderBy(e => e)
.ToList()
}
).OrderBy(s => s.list.FirstOrDefault())
.ToList();
EDIT
So, the inner lists are sorted by the text value of the eList; and the outer list is sorted by the first element of the inner list
EDIT
var s2=(from d1 in sam
select new
{
name = d1.name,
id = d1.id,
list =
(
from d2 in d1.list
join e in eList on d2 equals e.id
select e.text
).OrderBy(item => item).ToList()
}).OrderBy(item => item.list.FirstOrDefault());

LINQ: Counting Total Number of Nested Elements

I have a data table that contains a one-to-many self referential relationship:
Plant
{
ID,
Name,
ParentID
}
I'm trying to create a linq query that will tell me the total number of descendants stemming from one Plant.
Example:
I have 5 plants:
One {ID = 1, Name = Pine, Parent = null};// This is the root
Two {ID = 2, Name = Evergreen, Parent = 1}
Three {ID = 3, Name = Winter Evergreen, Parent = 2}
Four {ID = 4, Name = Christmas Tree, Parent = 1}
Five {ID = 5, Name = Maple, Parent = null};// This is the root
When I call my LINQ query with an input of ID = 1, I want it to return 3, because there are 3 descendants of One; Two, Three and Four. Five is not a decedent of One.
The only way I can think about doing this involves nested recursive linq queries on the inner results. I have no idea how to do this and I feel as though there is an easier way.
I'm using C# 4.0 and LINQ if that matters.
If you were using SQL Server, the query you would want constructed would be:
DECLARE #TargetElementId int
SET #TargetElementId = 1;
WITH Plants AS
(
SELECT ID, Name, ParentId
FROM PlantsTable
WHERE ParentId = #TargetElementId
UNION ALL
SELECT ID, Name, ParentId
FROM PlantsTable
INNER JOIN Plants ON PlantsTable.ParentId = Plants.ID
)
SELECT COUNT(ID) - 1 FROM Plants
I don't believe this can be done with LinqToSql, see Common Table Expression (CTE) in linq-to-sql?, which is a question of a similar nature.
EDIT adding code that supports LINQ to SQL thanks to #Kirk Woll comment.
class Program
{
private static Table<Plant> plants;
static void Main(string[] args)
{
using (var dataContext = new DataClasses1DataContext())
{
plants = dataContext.Plants;
var treesNodes = GetTreesNodes(plants.Where(plant => plant.ID == 1));
Console.WriteLine(string.Join(",",
treesNodes.Select(plant => plant.ID)));
}
}
public static IEnumerable<Plant> GetTreesNodes(IEnumerable<Plant> roots)
{
if(!roots.Any())
{
return roots;
}
var children = roots.SelectMany(root =>
plants.Where(plant => plant.Parent == root));
return roots.Union(GetTreesNodes(children));
}
}
Previous version that match LINQ to Objects:
This method can provide all the nodes in the tree:
public IEnumerable<Plant> GetTreesNodes(IEnumerable<Plant> roots)
{
if(!roots.Any())
{
return Enumerable.Empty<Plant>();
}
var rootsIds = roots.Select(root => root.ID);
var children = plants.Where(plant => rootsIds.Contains(plant.Parent));
return roots.Union(GetTreesNodes(children));
}
It can be used as in the following example:
[Test]
public void ExampleTest()
{
var One = new Plant() {ID = 1, Name = "Pine", Parent = 0};
var Two = new Plant() {ID = 2, Name = "Evergreen", Parent = 1};
var Three = new Plant() {ID = 3, Name = "Winter Evergreen", Parent = 2};
var Four = new Plant() {ID = 4, Name = "Christmas Tree", Parent = 1};
var Five = new Plant() {ID = 5, Name = "Maple", Parent = 0};
plants = new []{One,Two,Three,Four,Five};
var nestedElements = GetTreesNodes(new[]{One});
var numOfNestedElementsWithoutRoot = nestedElements.Count()-1;
Assert.That(numOfNestedElementsWithoutRoot, Is.EqualTo(3));
}
The code assumes there are no cyclic references.

Categories