Easiest way to create a nested list from flat data - c#

Assume I have the following information in a flat format, for example a list of objects:
List<Line> lines = new List<Line>
{
new Line{Id = 1, Level = 0},
new Line{Id = 2, Level = 1},
new Line{Id = 3, Level = 1},
new Line{Id = 4, Level = 2},
new Line{Id = 5, Level = 2},
new Line{Id = 6, Level = 1},
new Line{Id = 7, Level = 1},
new Line{Id = 8, Level = 2},
new Line{Id = 9, Level = 1}
};
Each object has an id and a level. What I want to end up with a nested list. For this I have a class which can have a list of children, based on the level.
public class NestedLine
{
public int Id;
public List<NestedLine> Children = new List<NestedLine>();
}
What's the easiest way to convert that flat list into a nested list?
EDIT: The only information on how to construct the list is the order of the lines and the level. This should be the result:
1
--2
--3
--4
--5
--6
--7
--8
--9

Here is my attempt.
Calling code
List<Line> lines = new List<Line>
{
new Line{Id = 1, Level = 0},
new Line{Id = 2, Level = 1},
new Line{Id = 3, Level = 1},
new Line{Id = 4, Level = 2},
new Line{Id = 5, Level = 2},
new Line{Id = 6, Level = 1},
new Line{Id = 7, Level = 1},
new Line{Id = 8, Level = 2},
new Line{Id = 9, Level = 1}
};
NestedLine nestedLine = Recusrsive(lines, 0);
Recursive Method
private NestedLine Recusrsive(List<Line> lines, int listPos)
{
NestedLine retVal = new NestedLine();
Line line = lines[listPos];
retVal.Id = line.Id;
for (int iItem = listPos + 1; iItem < lines.Count; iItem++)
if (lines[iItem].Level == line.Level + 1)
retVal.Children.Add(Recusrsive(lines, iItem));
else if (lines[iItem].Level <= line.Level) return retVal;
return retVal;
}

Can you provide a little more information here? This looks to me like you are really dealing with a tree structure:
1
-> 2
-> 3
-> -> 4
-> -> 5
-> 6
-> 7
-> -> 8
-> 9
am i correct? and if so, what is the rule for determining the parent of the lower level nodes?
The first thing that comes to mind is to use a recursive constructor for your NestedLine class:
public NestedLine(List lines)

Use a stack to keep track what was the last item added at each level, pushing and popping as you go along:
var stack = new Stack<NestedLine>();
foreach (var line : lines) {
while (stack.Count > line.Level) {
// Pop items until the top element is one up from current level
stack.Pop()
}
var child = new NestedLine{Id = line.Id};
if (stack.Count > 0) {
// if there is a parent, add the child to its children
stack.Peek().Children.Add(child);
}
// add current line as the deepest item
stack.Push(child);
}
NestedLine root;
while (stack.Count) {
root = stack.Pop();
}
Disclaimer: I haven't actually done much C# programming so this might contain some trivial errors.

Related

Visual Studio / Reflection / Serialization: Can I export values from a debug session to use them for unit testing?

I had to write many dozens of lines of code (see here: https://dotnetfiddle.net/RiVx2E) to generate a few lines of sample data.
In this specific case I could manually export the output variable (see the whole code on Fiddler) in this way:
new List {
{ IDMacroTab = 1, IDTab = 1, IDSIot = 2 }
{ IDMacroTab = 1, IDTab = 2, IDSIot 1}
{ IDMacroTab = 1, IDTab = 2, IDSIot = 2}
{ IDMacroTab = 1, IDTab = 2, IDSIot = 3}
{ IDMacroTab = 2, IDTab = 1, IDSIot = 1}
{ IDMacroTab = 2, IDTab = 1, IDSIot = 2 }
{ IDMacroTab = 2, IDTab = 2, IDSIot = 1}
{ IDMacroTab = 2, IDTab = 2, IDSIot = 2}
{ IDMacroTab = 2, IDTab = 2, IDSIot = 3}
{ IDMacroTab = 3, IDTab = 1, IDSIot = 1}
{ IDMacroTab = 3, IDTab = 1, IDSIot = 2}
{ IDMacroTab = 3, IDTab = 2, IDSIot = 1}
{ IDMacroTab = 3, IDTab = 2, IDSIot = 2}
{ IDMacroTab = 3, IDTab = 2, IDSIot = 3}};
Is there any workaround that allows to serialize an object to the c# lines of code required to populate it?
Thrid Solution: ObjectDumper.NET
I might have another solution for you. I was using OmarElabd/ObjectExporter in previous projects with good results but there were problems with some kind of types (e.g. serializations of DateTime didn't work properly at the time). Performance was key too: Some generated objects had a size of 50k lines of code (yeh, don't mention this is too big for a single test data class, I know, but...).
After all, I started writing my own ObjectDumper which you can find published as a NuGet package here. The source code is hosted on github, so feel free to contribute.
https://www.nuget.org/packages/ObjectDumper.NET/
The idea of ObjectDumper.NET is that you can dump basically any C# object back to C# initializer code. Compared to ObjectExporter (which is a Visual Studio plug-in), ObjectDumper is installed as NuGet Package. This allows ObjectDumper to be used at runtime - not only at the time of code creation.
Example Usage:
[Fact]
public void SerializeObjectsToInitializerCode()
{
// Create C# object
var testObjects = new List<TestObject>
{
new TestObject {IDMacroTab = 1, IDTab = 1, IDSIot = 2},
new TestObject {IDMacroTab = 1, IDTab = 2, IDSIot = 1},
new TestObject {IDMacroTab = 1, IDTab = 2, IDSIot = 2}
};
// Pass it to ObjectDumper, choose DumpStyle.CSharp to generate C# initializer code
var dump = ObjectDumper.Dump(testObjects, DumpStyle.CSharp);
// Print to console, write to file, etc...
_testOutputHelper.WriteLine(dump);
}
First solution (partial)
I've found this question which can be pretty useful but just for some kind of object (i.e. lists)
In Visual Studio when debugging C# code, can I export a List or a Dictionary in xml,csv or text format easily?
Second solution
And also this plugin ObjectExporter (last update 2017; checked at 2018)
https://marketplace.visualstudio.com/items?itemName=OmarElabd.ObjectExporter

Find common items in multiple lists in C# linq

I searched, but I found only answers which related to two lists. But what about when they are more than two?
List 1 = 1,2,3,4,5
List 2 = 6,7,8,9,1
List 3 = 3,6,9,2,0,1
List 4 = 1,2,9,0,5
List 5 = 1,7,8,6,5,4
List 6 = 1
List 7 =
How to get the common items? as you can see one of them is empty, so the common will be empty, but I need to skip empty lists.
var data = new List<List<int>> {
new List<int> {1, 2, 3, 4, 5},
new List<int> {6, 7, 2, 8, 9, 1},
new List<int> {3, 6, 9, 2, 0, 1},
new List<int> {1, 2, 9, 0, 5},
new List<int> {1, 7, 8, 6, 2, 5, 4},
new List<int> {1, 7, 2}
};
List<int> res = data
.Aggregate<IEnumerable<int>>((a, b) => a.Intersect(b))
.ToList();
The type of Aggregate is explicitly given, otherwise aggregation of two Lists would have to be List too. It can be easily adapted to run in parallel:
List<int> res = data
.AsParallel<IEnumerable<int>>()
.Aggregate((a, b) => a.Intersect(b))
.ToList();
EDIT
Except... it does not run in parallel. The problem is operations on IEnumerable are deferred, so even if they are logically merged in parallel context, the actual merging occurs in the ToList(), which is single threaded. For parallel execution it would be better to leave IEnumerable and return to the Lists:
List<int> res = data
.AsParallel()
.Aggregate((a, b) => a.Intersect(b).ToList());
You can chain Intersect:
List<int> List1 = new List<int> {1, 2, 3, 4, 5};
List<int> List2 = new List<int> { 6, 7, 8, 9, 1 };
List<int> List3 = new List<int> { 3, 6, 9, 2, 0, 1 };
List<int> List4 = new List<int> { 1, 2, 9, 0, 5 };
List<int> List5 = new List<int> { 1, 7, 8, 6, 5, 4 };
List<int> List6 = new List<int> { 1 };
List<int> common = List1
.Intersect(List2)
.Intersect(List3)
.Intersect(List4)
.Intersect(List5)
.Intersect(List6)
.ToList();
var data = new [] {
new List<int> {1, 2, 3, 4, 5},
new List<int> {6, 7, 8, 9, 1},
new List<int> {3, 6, 9, 2, 0, 1},
new List<int> {1, 2, 9, 0, 5},
new List<int> {1, 7, 8, 6, 5, 4},
new List<int> {1},
new List<int> {},
null
};
IEnumerable<int> temp = null;
foreach (var arr in data)
if (arr != null && arr.Count != 0)
temp = temp == null ? arr : arr.Intersect(temp);
One way is to use a HashSet. You can put the items of the first collection in the hash, then iterate each collection after the first and create an new hash that you add items from the current collection to if it's in the hash. At the end you assign that common hash set to the overall one and break if it's every empty. At the end you just return the overall hash set.
public IEnumerable<T> CommonItems<T>(IEnumerable<IEnumerable<T>> collections)
{
if(collections == null)
throw new ArgumentNullException(nameof(collections));
using(var enumerator = collections.GetEnumerator())
{
if(!enumerator.MoveNext())
return Enumerable<T>.Empty();
var overall = new HashSet<T>(enumerator.Current);
while(enumerator.MoveNext())
{
var common = new HashSet<T>();
foreach(var item in enumerator.Current)
{
if(hash.Contains(item))
common.Add(item);
}
overall = common;
if(overall.Count == 0)
break;
}
return overall;
}
}

C# merge multiple lists by id and summing the property

I got a grapic class which looks like this:
public class Graphic
{
public int id { get; set; }
public int Counter { get; set; }
}
and now i have 3 lists of grapic:
public void myFunction()
{
List<Graphic> l1= new List<Graphic> ();
List<Graphic> l2= new List<Graphic> ();
List<Graphic> l3= new List<Graphic> ();
l1.Add(new Graphic {id = 1, Counter = 4});
l1.Add(new Graphic {id = 2, Counter = 2});
l1.Add(new Graphic {id = 3, Counter = 9});
l1.Add(new Graphic {id = 4, Counter = 1});
l2.Add(new Graphic {id = 1, Counter = 1});
l2.Add(new Graphic {id = 2, Counter = 2});
l2.Add(new Graphic {id = 3, Counter = 3});
l2.Add(new Graphic {id = 5, Counter = 1});
l3.Add(new Graphic {id = 1, Counter = 12});
l3.Add(new Graphic {id = 7, Counter = 1});
l3.Add(new Graphic {id = 8, Counter = 3});
l3.Add(new Graphic {id = 4, Counter = 1});
}
And i need a code which will merge these 3 lists, each graphic with it's ID and the counter property will be summed up, so the result will be:
var mergedList = {
Graphic {id = 1, Counter = 17},
Graphic {id = 2, Counter = 4},
Graphic {id = 3, Counter = 12},
Graphic {id = 4, Counter = 2},
Graphic {id = 5, Counter = 1},
Graphic {id = 7, Counter = 1},
Graphic {id = 8, Counter = 3}
}
any idea will help me.
Is there a way to do that efficiently with LINQ?
Use .Concat to merge the 3 lists and then GroupBy to get all the Counter values of the different ids:
var result = l1.Concat(l2)
.Concat(l3)
.GroupBy(item => new { item.id, item.Geometry }, item => item.Counter)
.Select(group => new Graphic
{
id = group.Key,
Counter = group.Sum()
}).ToList();
See collection initializer for a neater way to initialize the lists:
List<Graphic> l1 = new List<Graphic>
{
new Graphic { id = 2, Counter = 2 },
new Graphic { id = 3, Counter = 9 },
new Graphic { id = 4, Counter = 1 },
new Graphic { id = 1, Counter = 4 },
};
I also suggest reading C# naming conventions for the naming of your function and the properties.

Adding data to an array of struct

I'm trying to add/remove data from an array of a defined struct.
struct process
{
public int Proc_Id;
public int Proc_BurstTime;
public int Proc_Priority;
public override string ToString()
{
return "ID: " + Proc_Id.ToString() + " Time: " + Proc_BurstTime.ToString() + " Prior: " + Proc_Priority.ToString();
}
};
readonly process[] ProcessList = new process[]
{
new process{ Proc_Id = 1, Proc_BurstTime = 3000, Proc_Priority = 1},
new process{ Proc_Id = 2, Proc_BurstTime = 5000, Proc_Priority = 2},
new process{ Proc_Id = 3, Proc_BurstTime = 1000, Proc_Priority = 3},
new process{ Proc_Id = 4, Proc_BurstTime = 10000, Proc_Priority = 4}
};
I see it is readonly (based on some Google searches, I have no other option.
I can modify the data, but how do I go about adding data to the ProcessList? I've tried ProcessList.Add(~), but that's not working.
Basically, what I'm exactly trying to do is populate this array of structs into a ListBox control. I have been able to initially populate it by using DataSource, I've also been able to modify the contents and "re-datasource" it to update the ListBox. However, I cannot ADD or REMOVE processes. Does anyone know what I can do?
You'll need to use a collection.
A List<T> would do just fine.
var processes = new List<process>();
processes.Add(new process
{
Proc_Id = 1,
Proc_BurstTime = 3000,
Proc_Priority = 1
});
Modifying your code a bit would result in this below.
List<process> ProcessList = new List<process>()
{
new process {Proc_Id = 1, Proc_BurstTime = 3000, Proc_Priority = 1},
new process {Proc_Id = 2, Proc_BurstTime = 5000, Proc_Priority = 2},
new process {Proc_Id = 3, Proc_BurstTime = 1000, Proc_Priority = 3},
new process {Proc_Id = 4, Proc_BurstTime = 10000, Proc_Priority = 4}
};
This collection type also has the Add method you are referring to in your question.
Using readonly prevents the variable, ProcessList from being assigned a new list. If you removed readonly you could create a new array with the new, desired size and copy the elements from the old array to it, then assign it to ProcessList. This is unlikely what you really want to do. The better solution would be, instead, to use a collection that allows you to add/remove elements. An array, once created, cannot have it's size changed. A List<T> (or some other generic collection), is built to allow new elements to be added or existing elements to be removed, with the size adjusting to reflect the number of current elements.
readonly List<process> ProcessList = new List<process>
{
new process { Proc_Id = 1, Proc_BurstTime = 3000, Proc_Priority = 1 },
new process { Proc_Id = 2, Proc_BurstTime = 5000, Proc_Priority = 2 },
new process { Proc_Id = 3, Proc_BurstTime = 1000, Proc_Priority = 3 },
new process { Proc_Id = 4, Proc_BurstTime = 10000, Proc_Priority = 4 }
};

Sorting nested Lists within object using c# Lambda Expressions

I have an object
TestCollection testCollection = new TestCollection()
{
new Test
{
quickTest =
new QuickTest
{
id = a,
sortField = 3,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 1},
new NestedQuickTest{sortField = 2},
new NestedQuickTest{sortField = 3},
new NestedQuickTest{sortField = 4},
}
}
},
new Test
{
quickTest =
new QuickTest
{
id = b,
sortField = 2,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 21},
new NestedQuickTest{sortField = 32},
new NestedQuickTest{sortField = 11},
new NestedQuickTest{sortField = 2},
}
}
},
new Test
{
quickTest =
new QuickTest
{
id = c,
sortField = 1,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 3},
new NestedQuickTest{sortField = 2},
new NestedQuickTest{sortField = 8},
new NestedQuickTest{sortField = 1},
}
}
},
};
1) I would like to sort this object using lambda expressions.
2) I would like to get back the object sorted by the first in Asc order and then by the in Asc order.
3) I would like to remove the last property in both nested List<> objects so there are only two objects in each.
I apologize if this is a little confusing but I would like to do something like this:
var sorted = testCollection.OrderBy(x => x.quickTest.sortField).ThenBy(y => y.quickTest.nestedQuickTest.Select(z => z.sortField)).Take(2);
The end result would be:
TestCollection testCollection = new TestCollection()
{
,
new Test
{
quickTest =
new QuickTest
{
id = c,
sortField = 1,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 1},
new NestedQuickTest{sortField = 2}
}
}
},
new Test
{
quickTest =
new QuickTest
{
id = b,
sortField = 2,
nestedQuickTest =
new List<NestedQuickTest>
{
new NestedQuickTest{sortField = 2},
new NestedQuickTest{sortField = 11}
}
}
}
};
Thanks in advance for your help, I can't seem to get my desired results and I know I am missing something obvious.
If I understand what you are saying you want to order the objects in MyOrderList by the property with the NestedObject, which is a collection of objects who should themselves ordered by the value of the AnotherNestedObject, which is also a list.
Well first of all you need to implement a Icomparer or something like that to set the rules of what makes one list bigger than the other.
See, you want to order objects in a list by a value, if its an int, well thats easy, but being a list, what are the rules to determine which one is bigger? Is it the one with more elements? The one with biggest sum of elements?
You need to figure out this first
EDIT:
Okay I think I understand your question better, you just want to order the outer lists objects by the int, and then also have the list inside each of those elements ordered too.
I think it would be easier if you did this in 2 steps something like:
var orderedTestCollection = testCollection.Orderby(tc=>tc.quickTest.SortField) // orders all the testCollection objects
//orders and only takes 2 elements of the lists inside the each test collection object
foreach(var tc in testCollection)
{
tc.quickTest.NestedQuickTest = tc.quickTest.NestedQuickTest.Orderby(nqt=>nqt.SortField).Take(2);
}

Categories