Select different objects from two collections - c#

I have the following class:
struct UserRecord
{
//--- common settings
int login; // login
int leverage; // leverage
int enable; // enable
}
And I have two lists:
List<UserRecord> base_data = new List<UserRecord>();
base_data.Add(new UserRecord(){login = 1, leverage = 1000, enable = 0});
base_data.Add(new UserRecord(){login = 2, leverage = 100, enable = 0});
base_data.Add(new UserRecord(){login = 3, leverage = 10, enable = 1});
base_data.Add(new UserRecord(){login = 4, leverage = 10000, enable = 0});
List<UserRecord> snapshot_data= new List<UserRecord>();
snapshot_data.Add(new UserRecord(){login = 1, leverage = 1000, enable = 1});
snapshot_data.Add(new UserRecord(){login = 2, leverage = 100, enable = 0});
snapshot_data.Add(new UserRecord(){login = 3, leverage = 10, enable = 1});
snapshot_data.Add(new UserRecord(){login = 4, leverage = 10000, enable = 1});
My goal is to filter the records, and get the two records in a new list, that are with different fields, in this case only the field 'enable' is different.
var filtered_data = new List<UserRecord>(); // here records with login 1 and 4 should go.
Do you have any suggestions?

You may look for Enumerable.Except() from System.Linq to find the differences between both enumerable.
using System.Linq;
List<UserRecord> filter_data = base_data.Except(snapshot_data)
.ToList();
Demo # .NET Fiddle
Another approach is if you just want to compare the difference in enable based on the items with the same login and leverage from the different lists.
You need to join both lists by the key(s) and query the record from the first list with the different enable value.
List<UserRecord> filter_data = (from a in base_data
join b in snapshot_data on new { a.login, a.leverage } equals new { b.login, b.leverage }
where a.enable != b.enable
select a
).ToList();
Demo Solution 2 # .NET Fiddle

Related

Check if a set exactly includes a subset using Linq taking into account duplicates

var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
subset.All(s => superset.Contains(s))
This code would return true, because 9 is included in the superset,but only once, I want an implementation that would take into account the duplicates, so it would return false
My thought was that you could group both sets by count, then test that the super group list contained every key from the sub group list and, in each case, the super count was greater than or equal to the corresponding subcount. I think that I've achieved that with the following:
var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
var subGroups = subset.GroupBy(n => n).ToArray();
var superGroups = superset.GroupBy(n => n).ToArray();
var basicResult = subset.All(n => superset.Contains(n));
var advancedResult = subGroups.All(subg => superGroups.Any(supg => subg.Key == supg.Key && subg.Count() <= supg.Count()));
Console.WriteLine(basicResult);
Console.WriteLine(advancedResult);
I did a few extra tests and it seemed to work but you can test some additional data sets to be sure.
Here is another solution :
var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
var subsetGroup = subset.GroupBy(x => x).Select(x => new { key = x.Key, count = x.Count() });
var supersetDict = superset.GroupBy(x => x).ToDictionary(x => x.Key, y => y.Count());
Boolean results = subsetGroup.All(x => supersetDict[x.key] >= x.count);
This works for me:
var subsetLookup = subset.ToLookup(x => x);
var supersetLookup = superset.ToLookup(x => x);
bool flag =
subsetLookup
.All(x => supersetLookup[x.Key].Count() >= subsetLookup[x.Key].Count());
That's not how sets and set operations work. Sets cannot contain duplicates.
You should treat the two arrays not as sets, but as (unordered) sequences. A possible algorithm would be: make a list from the sequence superset, then remove one by one each element of the sequence subset from the list until you are unable to find such an element in the list.
bool IsSubList(IEnumerable<int> sub, IEnumerable<int> super)
{
var list = super.ToList();
foreach (var item in sub)
{
if (!list.Remove(item))
return false; // not found in list, so sub is not a "sub-list" of super
}
return true; // all elements of sub were found in super
}
var subset = new[] { 9, 3 };
var superset = new[] { 9, 10, 5, 3,1, 3, 3 };
var isSubSet = IsSubList(subset, superset);

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);
}

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.

Easiest way to create a nested list from flat data

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.

Categories