C# Sorting IEnumerable of IEnumerables - c#

I have an object that looks something like this:
public class MyObj
{
public string Title { get; set; }
public IEnumerable<Section> Sections { get; set; }
}
public class Section
{
public string Title { get; set; }
public IEnumerable<Item> Items { get; set; }
public int SortOrder { get; set; }
}
public class Item
{
public string Title { get; set; }
public int SortOrder { get; set; }
}
Essentially I end up with an IEnumerable of sections, which in turn contains an IEnumerable of items. Both the list of sections and the items need to be sorted by their respective SortOrder properties.
I know that I can sort the sections by doing obj.Sections.OrderBy(s => s.SortOrder) but then I can't work out how to sort the items within each section too.
The context is that I'm writing a Sort function that takes an unsorted MyObj and returns one with both the sections and the items sorted.
public MyObj Sort(MyObj unsortedObj)
{
var sortedObj = unsortedObj.....
return sortedObj;
}
The expected data structure would be something like this:
- Section1
- Item1
- Item2
- Section2
- Item1
- Item2

It would be convenient for you to add methods that creates copies of these objects except for one property being different:
// in MyObj
public MyObj WithSections(IEnumerable<Section> sections) =>
new MyObj {
Title = this.Title,
Sections = sections
};
// in Section
public Section WithItems(IEnumerable<Items> items) =>
new Section {
Title = this.Title,
Items = items,
SortOrder = this.SortOrder
};
First, sort the sections
var sortedSections = unsortedObj.Sections.OrderBy(x => x.SortOrder);
Then for each of those sorted sections, transform them with Select so that their items are also sorted:
var sortedSectionsAndItems = sortedSections.Select(x => x.WithItems(x.Items.OrderBy(y => y.SortOrder)));
Now you can return a MyObj with the sorted sections and items:
return unsortedObj.WithSections(sortedSectionsAndItems);

Related

C# group by a column and form hierarchical data with other columns

I am trying to group a column and form the the rest of the columns as child, hierarchical data:
I am trying to group by Code and form the parent and child relationship from a flat list, below is the hierarchical data I am trying to form:
source list:
public class ItemAssignmentFlatList
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need to convert above flat list into below List of hierarchical data:
public class ItemInfo
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public List<TaxInfo> TaxPlan { get; set; }
}
public class TaxPlan
{
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need hierarchical list with above flat data list with C# extension methods.
I have below code, but looking for clean code to reduce number of lines:
var items= results.GroupBy(x => new { x.Code, x.Type });
List<ItemInfo> result = new List<ItemInfo>();
foreach (var group in items)
{
var taxPlans = group.
Select(y => new TaxPlan
{
TaxArea = y.TaxArea,
ItemCode = y.ItemCode
});
var itemInfo= new ItemInfo
{
Code = group.FirstOrDefault().Code,
Type = group.FirstOrDefault().Type,
Description = group.FirstOrDefault().Description,
TaxPlan = taxPlans.ToList()
};
result.Add(itemInfo);
}
Something like this?:
var input = new List<ItemAssignmentFlatList>(){
new ItemAssignmentFlatList{
Code = 1,
Area = "a"
},
new ItemAssignmentFlatList{
Code = 1,
Area = "b"
},
new ItemAssignmentFlatList{
Code = 2,
Area = "c"
}
};
input
.GroupBy(
x => x.Code,
(int code, IEnumerable<ItemAssignmentFlatList> items) =>
{
var first = items.FirstOrDefault();
var key = new ItemInfo
{
Code = first.Code
//, ...
};
var plan = items.
Select(y => new TaxPlan
{
Area = y.Area
//, ...
});
return new
{
key = key,
items = plan
};
}
).Dump();
Whenever you have a sequence of similar object, and you want to make "Items with their SubItems", based on common properties in your source sequence, consider to use one of the overloads of Enumerable.GroupBy
Because you don't just want "Groups of source items" but you want to specify your output, consider to use the overload that has a parameter resultSelector.
parameter keySelector: what should all elements in a group have in common
parameter resultSelector: use the common thing, and all elements that have this common thing to make one output element.
.
IEnumerable<ItemAssignmentFlatList> flatItemAssignments = ...
IEnumerable<ItemInfo> items = flatItemAssignments
// make groups with same {Code, Type, Description}
.GroupBy(flatItemAssignment => new {Code, Type, Description},
// parameter resultSelector: take the common CodeTypeDescription,
// and all flatItemAssignments that have this common value
// to make one new ItemInfo
(codeTypeDescription, flatItemAssignmentsWithThisCodeTypeDescription) => new ItemInfo
{
Code = codeTypeDescription.Code,
Type = codeTypeDescription.Type,
Description = codeTypeDescription.Description,
TaxPlans = flatItemAssignmentsWithThisCodeTypeDescription
.Select(flatItemAssignment => new TaxPlan
{
ItemCode = flatItemAssignment.ItemCode,
EffectiveDate = flatItemAssignment.EffectiveDate,
Area = flatItemAssignment.Area,
...
})
.ToList(),
});

Possible to add a condition for linked table fields in LINQ

Can someone suggest me a solution to add condition for reference table items in linq.
I have a master table called TourPackage, which include
public class TourPackage{
public int TourID { get; set; }
public string TourName { get; set; }
public List<IncludedItems> IncludedItems { get; set; }
}
Every tour package contain some selected items reference like
public class IncludedItems {
public int TourID { get; set; }
public int IncludedID { get; set; }
public Included Included { get; set; }
}
All included item should have a reference to Included table for lookup reference
public class Included {
public int IncludedID { get; set; }
public string IncludedValue { get; set; }
}
now i have set of IncludedID like [1,2,3], Is it possible to filter TourPackage based on IncludedID.
Thanks in advance
You can use following code
I have sample array(i.e example) which contains ID's we check if current Id(i.e ele.Included.IncludedID) is present in the array of id's.
listex.Where(x => x.IncludedItems.Any(ele => example.Contains(ele.Included.IncludedID))).ToList();
sample:-
int[] example = new int[3];
example[0] = 123;
example[1] = 456;
example[2] = 789;
List<TourPackage> listex = new List<TourPackage>();
List<TourPackage> filterList = listex.Where(x => x.IncludedItems.Any(ele => example.Contains(ele.Included.IncludedID))).ToList();
Have you tried using something like:
var myIds = new List<int> {123,456};
var result = context.TourPackages
.Where(x => x.IncludedItems.Any(a => a.Included !=null && myIds.Contains(a.Included.IncludedId)))
.ToList();
You might have to include some relations manually depending if you're lazy loading is setup or not.
More info at https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx

C# Merge properties from two (or more) lists based on common property

I have three lists of objects each of which are linked to one other list by a single common property, Parent and Node in the following hierarchy: Model -> Intermediate -> Results. I have the following code:
class Result
{
public string Name { get; set; }
public int Node{ get; set; }
public int Parent{ get; set; }
}
class Intermediate
{
public int Node{ get; set; }
public int Parent{ get; set; }
}
class Model
{
public string Name { get; set; }
public int Node{ get; set; }
public int Parent{ get; set; }
}
public class Example
{
public static void Main()
{
List<Result> results = new List<Result>();
List<Intermediate> intermediates = new List<Intermediate>();
List<Model> models = new List<Model>();
// Example objects in the list
results.Add(new Result() { Name = "", Parent = 21, Node = 101 });
intermediates.Add(new Part() { Parent = 11, Node = 21 });
models.Add(new Part() { Name = "ABCD", Parent = 1, Node = 11 });
...
}
}
As can be seen the Model object links to Intermediate object via model.Node and intermediate.Parent, and the Intermediate object links to Results object via intermediate.Node and results.Parent.
Note the lists can contain thousands of items, each added using a similar format as above.
What I want to be able to do is add the names from the Model objects in list to the matching Results objects in the results list.
My thinking is that I can loop through each object in the Intermediate list and find (using LINQ) the Result object where intermediate.Node = result.Parent, and then either replace the value of the result.Parent with intermediate.Parent, or add a new Grandparent property to the Result object in which to put the intermediate.Parent. Then repeat the process looping through each of the objects in the models list finding the matching Result object and adding the Name.
So I guess my question is, is this the best way of doing this, or is there a more efficient way? I have many lists where the same will have to be repeated, so was wondering if there was a better way as it can be quite slow looping through every object. Also is there a way to get from the first list directly to the third list.
I hope this is well enough explained. I am quite a beginner when it comes to C#.
You actually have Results -> Intermediate -> Model instead of Model -> Intermediate -> Results.
To speed the process of removing the Intermediate, build a dictionary. Then you can do a simple select on Results using the dictionary to convert.
var intermediateDict=intermediates.ToDictionary(key=>key.Node,val=>val.Parent);
var newresults=results.Select(r=>new Result {
Name=r.Name,
Node=r.Node,
Parent=intermediateDict[r.Parent]
});
You can also do joins to get the final answer.
It looks a foreach loop can be used to get to the result (leaf) nodes and assign the model name to result nodes:
var theModel = models.First(); // select a model
foreach (var interm in intermediates.Where(x => x.Parent == theModel.Node))
{
foreach (var res in results.Where(x => x.Parent == interm.Node))
{
res.Name = theModel.Name;
}
}
Smells like Composite Pattern what you talk about here.
And you can use HashSet to keep your values to perform it fast.
public class Item
{
public Item(int itemNode)
{
Node = itemNode;
Children = new HashSet<Item>();
}
public int Node { get; set; }
public Item Parent { get; set; }
private HashSet<Item> Children { get; set; }
public bool Add(Item item)
{
item.Parent = this;
return Children.Add(item);
}
public List<Item> Find(Func<Item, bool> predicate)
{
var found = new List<Item>();
if (predicate(this)) found.Add(this);
Collect(predicate, found);
return found;
}
public void Collect(Func<Item, bool> predicate, List<Item> collected = null)
{
collected = collected ?? new List<Item>();
collected.AddRange(Children.Where(predicate).ToList());
foreach (var child in Children)
{
child.Collect(predicate, collected);
}
}
}
public class Model : Item //this is your model
{
public string Name { get; set; }
public Model(int itemNode, string name) : base(itemNode)
{
Name = name;
}
public List<Item> GetNamesMatchingWith(Func<Item, bool> predicate)
{
return Find(predicate);
}
}
public class Example
{
public static void Main()
{
var root = new Model(0, "root");
var one = new Model(1, "1");
var two = new Model(2, "2");
var tree = new Model(3, "3");
root.Add(one);
root.Add(two);
root.Add(tree);
var a = new Model(4, "a");
var b = new Model(5, "b");
two.Add(a);
two.Add(b);
var namesMatchingWith = root.GetNamesMatchingWith(x=> x.Parent!=null && x.Parent.Node == 2);
Console.ReadKey();
}
}
Hope it inpires you..

Lambda expression - select single object with a IEnumerable<> property

Is it possible to select a single object and populate a containing IEnumerable property with a single Lambda expression?
Something like this:
var someViewModel = _repository.Table.Where(x => x.Id == someId)
.Select(new ListViewModel(){
GroupId = x.Group.Id,
GroupTitle = x.Group.Title
List = ?? // Select new SubViewModel and add it to IEnumerable<SubViewModel>
})
The result I'm after is a new object (ListViewModel in this case) that contains 3 properties. "List" being a collection of newly selected objects.
Is this possible? Am I coming at this from the wrong angle?
Thanks!
Update:
Let me try again :) Keep in mind that my naming is fictional here. Given the following two classes I would like to construct a DB query using a Lambda expression which creates a single "ListViewModel" that contains a collection of "SubViewModel". Does this help clarify?
public class SubViewModel
{
public int Id { get; set; }
public string Title { get; set; }
}
public class ListViewModel
{
public int GroupId { get; set; }
public string GroupTitle { get; set; }
public IEnumerable<SubViewModel> List { get; set; }
}
I'm not sure if I understand your question correctly but here is what I am thinking, you need to create a new IEnumberable and add the item to that collection.
var someViewModel = _repository.Table.Where(x => x.Id == someId)
.Select(new ListViewModel()
{
GroupId = x.Group.Id,
GroupTitle = x.Group.Title
List = new List<SubViewModel> { new SubViewModel(x) }
});

RavenDB cross entity query matching on non-identifier property

I have the following entity collections in RavenDB:
public class EntityA
{
public string Id { get; set; }
public string Name { get; set; }
public string[] Tags { get; set; }
}
public class EntityB
{
public string Id { get; set; }
public string Name { get; set; }
public string[] Tags { get; set; }
}
The only thing shared is the Tags collection: a tag of EntityA may exist in EntityB, so that they may intersect.
How can I retrieve every EntityA that has intersecting tags with EntityB where the Name property of EntityB is equal to a given value?
Well, this is a difficult one. To do it right, you would need two levels of reducing - one by the tag which would expand out your results, and another by the id to collapse it back. Raven doesn't have an easy way to do this.
You can fake it out though using a Transform. The only problem is that you will have skipped items in your result set, so make sure you know how to deal with those.
public class TestIndex : AbstractMultiMapIndexCreationTask<TestIndex.Result>
{
public class Result
{
public string[] Ids { get; set; }
public string Name { get; set; }
public string Tag { get; set; }
}
public TestIndex()
{
AddMap<EntityA>(entities => from a in entities
from tag in a.Tags.DefaultIfEmpty("_")
select new
{
Ids = new[] { a.Id },
Name = (string) null,
Tag = tag
});
AddMap<EntityB>(entities => from b in entities
from tag in b.Tags
select new
{
Ids = new string[0],
b.Name,
Tag = tag
});
Reduce = results => from result in results
group result by result.Tag
into g
select new
{
Ids = g.SelectMany(x => x.Ids),
g.First(x => x.Name != null).Name,
Tag = g.Key
};
TransformResults = (database, results) =>
results.SelectMany(x => x.Ids)
.Distinct()
.Select(x => database.Load<EntityA>(x));
}
}
See also the full unit test here.
There is another approach, but I haven't tested it yet. That would be to use the Indexed Properties Bundle to do the first pass, and then map those results for the second pass. I am experimenting with this in general, and if it works, I will update this answer with the results.

Categories