I am using a nested set model and want to be able to select 10 parent items, as well as 10 child items for each parent, which are ordered.
I am trying to write a nested comment system, whereby a reply to a comment will be signified by having left and right values within its 'parent' comment.
I am having a real headache reducing the load times of large lists of items being retrieved and believe if I could do the above in a single Linq statement then I might be able to save a lot more time than making repeated calls to the db to get 10 children for each parent.
I use a this statement to get the parent(or root items) from the datacontext.
var query = context.Items
.Where(x => !context.Items.Any(y => y.LeftPos < x.LeftPos
&& y.RightPos > x.RightPos))
.OrderBy(x => x.Id)
.Take(pageSize)
.ToList();
The above code is getting the outermost (parent) items in the nested set model by checking there are not any other items with left and right values outside of theirs.
So, rather than looping through the parentItems with a foreach (which I am doing currently) and making 10 or (pageSize) calls to the db on each iteration, how can I take 10 children of each parent node with a Linq statement?
EDIT:
I am using the nested set model. My items have left and right positions (leftPos and rightPos). So a child is an object with left and right values within the left and right values of another object, which in turn would be the parent.
1 a 4
2 b 3
so if I have a lot of items
1 a 4
2 b 3
5 c 10
6 d 7
8 e 9
11 f 14
12 g 13
....
Is there a way I can select x amount of children from each parent using Linq?
Any help appreciated
class Program
{
static void Main(string[] args)
{
List<A> lst = new List<A>();
for (int j = 1; j < 4; j++)
{
var tmp = new A() { Value = j * 1000 };
for (int i = 0; i < 150; i++)
{
tmp.SubItems.Add(new B { Value = i + 1, Parent = tmp });
}
lst.Add(tmp);
}
List<B> result = lst.SelectMany(x => x.SubItems.Take(10)).ToList();
}
}
public class A
{
public A()
{
SubItems = new List<B>();
}
public int Value { get; set; }
public List<B> SubItems { get; set; }
}
public class B
{
public int Value { get; set; }
public A Parent { get; set; }
}
not sure if this is what you want. this way you get a collection of subitems. 10 subitems of each parent. you can access the parents with the .Parent property of each subitem...
Related
I have a flat structure of data which I have retrieved with a QueryExpression and LinkEntity (InnerJoin). I have aliased the child with "element"
So my data looks like this:
parentIdPK parentStatus element.ChildIdPK element.parentIdFK
1 100 10 1
1 100 11 1
1 100 12 1
2 100 13 2
2 100 14 2
3 100 15 3
3 100 16 3
3 100 17 3
So bascially I have a Parent/Child structure and I want to push this data into my own classes:
public class ExistingProposal
{
public Guid parentIdPK { get; set; }
public int parentStatus { get; set; }
public List<ExistingElement> Elements { get; } = new List<ExistingElement>();
}
public class ExistingElement
{
public Guid ChildIdPK { get; set; }
public Guid parentIdFK { get; set; }
}
So in general this would lead to have one ExistingProposal with N ExistingGRProposalElement's
Ho can I achieve this in the best way? I have tried with linq but I'm struggling pretty much with this.
What I am trying actually is to group the data with linq:
var groups = from a in result.Entities
orderby a.Attributes["parentId"]
group a by a.Attributes["parentId"] into g
select new { g };
The problem I have actually is I dont know exactly from where to start to create the needed class structure.
Maybe somebody can point me to the right direction?
Any hint is highly appreciated.
Your question isn't very clear, but, if I understood well the following expression will do the trick for you :
var groups = from a in result.Entities
group a by a.Attributes["parentId"] into g
select new ExistingProposal {
parentIdPK = a.Key,
parentStatus = (int)a.FirstOrDefault().Attributes["parentStatus"],
Elements = (from y in g
select new ExistingElement {
ChildIdPK = y.Attributes["element.ChildIdPK"],
parentIdFK = a.Key
}).ToList()
};
You'll need to add a setter to your Elements property in ExistingProposal
You don't need to order before grouping
You should rename intermediate vars (y, g, a, etc.) to more meaningful ones
I would like to loop thru my records by Price (records are inserted by price, down to the penny) upon selecting a record, also grab the records one penny above and one penny below (if available), and total the Quantity. If the Quantity of these combined records is greater than a value, select it for further processing.
I think Grouping would work, but I'm not sure where to start. Could someone please point me in the right direction?
// Query the List
var CombinedQtyGT5K = (from a in TrackPrice
orderby a.Quantity descending
where a.Quantity > 5000
select new
{
a.Price,
a.Quantity,
});
foreach (var myprice in CombinedQtyGT5K )
{
//Process prices
}
public class PriceLevel
{
public decimal Price { get; set; }
public int Quantity { get; set; }
}
The first thing would be the grouping
var selected;
var group = CombinedQtyGT5K.GroupBy(x => x.Price);
//Not sure if you want to group it this way, cant comment so ill be guessing some
//things in this answer
Then add the loop
int c = group.count();
for(i=0; i < c, i++){
//add validation if up and down records avalible
//if c > 0 for example
if(group[c].Quantity + group[c-1].Quantity + group[c+1].Quantity > MinValue ){
selected = group[c];
//call the method to do your proccesing here
}
}
I'm currently working on a project and working some lambda expressions (basics). And I have this requirement that I need to display the data based on hierarchy (parent and child).
Current Class:
public class Phones
{
public int ID { get; set; }
public string Phone { get; set; }
public int ChildOf { get; set; }
}
Current Data
ID Phone ChildOf
1 Samsung 0
2 Apple 0
3 GalaxyS3 1
4 GalaxyS4 1
5 5S 2
6 iPhone 6+ 2
I'm expecting a result of:
Results
**Samsung**
GalaxyS3
GalaxyS4
**Apple**
5S
iPhone6+
You can do this using the linq as below.
var result = phoneList.Where(x=>x.ChildOf!=0).GroupBy(x=>x.ChildOf).Select(g=> new {Key = phoneList.FirstOrDefault(x=>x.ChildOf==0 && x.ID ==g.Key).Phone,Value = g.Select(x => x.Phone)});
But mind you phoneList.FirstOrDefault(x=>x.ChildOf==0) this is happening just because you want the Phone Name for the grouping Key.
You can do the separate dictionary to get the values and use it. But if you have the parent ID then why use the name. You can use the parent ID whenever you required from the group key. IF you are to do this,
var result = phoneList.Where(x=>x.ChildOf!=0).GroupBy(x=>x.ChildOf).Select(g=> g.Key,g.ToList());
And when you need to show that on the UI Just replace the Key with the matching parent ID. That would reduce the lot of performance cost.
Would something like this suffice:
var lookup = phoneData.Where(e => e.ChildOf == 0)
.ToDictionary(id => id.Id, value => value.Phone);
var result = phoneData.Where(e => e.ChildOf > 0)
.GroupBy(e => lookup[e.ChildOf])
.Select(g => new { Parent = g.Key, Children = g.Select(x => x.Phone) });
Where phoneData is an enumerable of your raw data.
I have two lists of Level, and I want to merge them into a single list with only unique
indexes, and having the higher highscore of the two.
The Level object.
public class Level
{
public int Index { get; set; }
public string User { get; set; }
public int Highscore { get; set; }
public Level(int index,string user, int highscore)
{
Highscore = highscore;
User = user;
Index = index;
}
}
I have this test code
List<Level> Levels = new List<Level>();
List<Level> otherlevels = new List<Level>();
Levels.Add(new Level(1, "Test", 1));
Levels.Add(new Level(2, "Test", 2));
Levels.Add(new Level(3, "Test", 4));
Levels.Add(new Level(4, "Test", 1));
otherlevels.Add(new Level(1, "Test", 4));
otherlevels.Add(new Level(2, "Test", 4));
otherlevels.Add(new Level(3, "Test", 1));
//some linq code here
What I want from the linq code is a list that will have these 4 items.
Level(1,"Test",4)
Level(2,"Test",4)
Level(3,"Test",4)
Level(4,"Test",1)
I managed to group by Index and select the first, but I dont know how to select the one with the max Highscore.
It sounds like you got 90% of the way there by grouping the levels. Now you just need to order them to grab the largest scoring result:
List<Level> newLevels = from x in Enumerable.Concat(Levels, otherLevels)
group x by x.Index into grp
select grp.OrderByDescending(x => x.HighScore).First()
In addition to being more concise than Thomas's solution, this does not have either of his limitations: it will work with lists containing any number of duplicates, and for instances where a level is present in one list but not the other.
You can do an outer join:
from level in levels
join other in otherLevels on level.Index equals other.Index into tmp
from other in tmp.DefaultIfEmpty()
select other == null
? level
: level.HighScore >= other.HighScore ? level : other;
This approach assumes two things:
each list contains only distinct ids
each item in otherLevels has a corresponding item in levels (the reverse doesn't need to be true)
I have a list of objects List<Parent> Parents.
The Parent class has a List<Child> Children.
Until now, I have applied paging to Parents using LINQ:
List<Parent> PageX = Parents.Skip(PageIndex * PageSize).Take(PageSize);
For instance, if PageSize=2, I have the following result:
--------------- Page 1 ----------------------
Parent 1
Child 1
Child 2
Child 3
Parent 2
Child 1
Child 2
--------------- Page 2 ----------------------
Parent 3
Child 1
Parent 4
Child 1
Child 2
What I want to achieve is the following:
--------------- Page 1 ----------------------
Parent 1
Child 1
Child 2
--------------- Page 2 ----------------------
Child 3
Parent 2
Child 1
--------------- Page 3 ----------------------
Child 2
Parent 3
Child 1
How can I achieve this?
You could use SelectMany:
var page = parents.SelectMany(p => p.Children)
.Skip(PageIndex * PageSize).Take(PageSize);
See a working fiddle here.
Update:
I did some research as this did interest me also. Using the following assumptions:
you are using EF
you have only one level of this parent->children relationship
your entities have all a Position property
you should be able to execute the following against the DB to get the items for a page with the correct order "in one query":
var pageItems = db.Parents.SelectMany(p => p.Children).Include(c => c.Parent)
.OrderBy(c => c.Parent.Position).ThenBy(c => c.Position)
.Skip(PageIndex * PageSize).Take(PageSize);
I did not test this code, as I have no DB here at the moment to test it, so please report back, if you can test it, if the assumtions are correct for your case.
I am presuming that both Parent and Child have a common property (say, Name) which is used to identify them?
In that case, you would have to flatten the hierarchy to include both parents and children info at the same list level.
So, presuming something like:
class Parent
{
public string Name { get; set; }
private readonly List<Child> _children = new List<Child>();
public List<Child> Children
{
get { return _children; }
}
}
class Child
{
public string Name { get; set; }
}
You would flatten it to a single IEnumerable<string> using:
var flattened = Parents
.Select(p => new [] { p.Name }.Concat(p.Children.Select(c => c.Name)))
.SelectMany(x => x);
And then you page it the way you did so far:
var results = flattened.Skip(PageIndex * PageSize).Take(PageSize);
foreach (var x in results)
Console.WriteLine(x);
It would probably be cooler if your Parent and Child both inherited from the same class or interface.