Not Equal two Generic Lists - c#

I have two generic lists. They have mostly different fields, but there are 4 fields they have in common. I want to get the list of items that are in one of the lists but not the other using those four fields as the definition of "equality".
Here was my attempt at solving the problem.
var unMatchedData = from liveLines in liveList
join oldList in comapreSnapshotList
on new {liveLines.ClientNo, liveLines.SequenceNo, liveLines.LineNo, liveLines.Text} equals
new {oldList.ClientNo, oldList.SequenceNo, oldList.LineNo, oldList.Text}
select new KNOWTXTS
{
ClientNo = liveLines.ClientNo,
SequenceNo = liveLines.SequenceNo,
LineNo = liveLines.LineNo,
Text = liveLines.Text
};

You can use Except to find the set difference.
var newElements = liveList.Select(l => new {
l.ClientNo, l.SequenceNo, l.LineNo, l.Text
});
var oldElements = comapreSnapshotList.Select(l => new {
l.ClientNo, l.SequenceNo, l.LineNo, l.Text
});
var newElementsInNew = newElements.Except(oldElements);
var deletedFromNew = oldElements.Except(newElements);
// if you need the original object in the list
var newElements = from obj in liveList
join newEle in newElementsInNew
on new {obj.ClientNo, obj.SequenceNo, obj.LineNo, obj.Text} equals newEle
select obj;
var deletedElements = from obj in comapreSnapshotList
join deletedEle in deletedFromNew
on new {obj.ClientNo, obj.SequenceNo, obj.LineNo, obj.Text} equals deletedEle
select obj;

Related

How to modify an object depends on join condition in linq

I have a list of objects and alist of integers. Objects have property called exists.
public bool exists;
I need to join this two lists using left join and if ids exist in two lists, then set "exists" property to true;
I prepred dotnetfiddle example:
https://dotnetfiddle.net/sE1RIl
Expected result is (pseudocode):
Item1.exists = true;
Item2.exists = true;
Item3.exists = false;
Probably I will need to add more left joins later, so I am interested in the most flexible way to achieve that.
You can map your properties using select statement:
IEnumerable<Item> items = new List<Item>()
{
new Item (){id =1, name = "Item1"},
new Item (){id =2, name = "Item2"},
new Item (){id =3, name = "Item3"}
};
List<int> ids = new List<int>() {1,2};
var param_1 = true;
var param_2 = false;
var param_3 = true;
var listOfItems = from item in items
join id in ids on item.id equals id
into result
from r in result.DefaultIfEmpty()
select new Item
{
id = item.id,
name = item.name,
exists = (param_1 == true) ? true
: (param_2 == false && param_3 == true) ? false
: true
};

Unable to get a distinct list that contains summed values

I have posted this earlier but the objective of what I am trying to achieve seems to have lost hence re-posting it to get explain myself better.
I have a collection that has duplicate productnames with different values. My aim is to get a list that would sum these productnames so that the list contains single record of these duplicates.
For e.g
If the list contains
Product A 100
Product A 200
The result object should contain
Product A 300
So as you can see in my code below, I am passing IEnumerable allocationsGrouped to the method. I am grouping by productname and summing the Emv fields and then looping it so that I created a new list of the type List and pass it to the caller method. The problem what I seeing here is on the following line of code Items = group. Items now contains original list without the sum. Hence the inner foreach loop runs more than ones because there are duplicates which defeats my purpose. I finally need to return result object that has non duplicate values which are summed based on the above criteria. Could you please tell me where I am going wrong.
private static List<FirmWideAllocationsViewModel> CreateHierarchy(string manStratName, IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationsGrouped, List<FirmWideAllocationsViewModel> result)
{
var a = allocationsGrouped
.Where(product => !string.IsNullOrEmpty(product.PRODUCT_NAME))
.GroupBy(product => product.PRODUCT_NAME)
.Select(group => new
{
ProductName = group.Key, // this is the value you grouped on - the ProductName
EmvSum = group.Sum(x => x.EMV),
Items = group
});
var b = a;
var item = new FirmWideAllocationsViewModel();
item.Hierarchy = new List<string>();
item.Hierarchy.Add(manStratName);
result.Add(item);
foreach (var ac in b)
{
var productName = ac.ProductName;
var emvSum = ac.EmvSum;
foreach (var elem in ac.Items)
{
var item2 = new FirmWideAllocationsViewModel();
item2.Hierarchy = new List<string>();
item2.Hierarchy.Add(manStratName);
item2.Hierarchy.Add(elem.PRODUCT_NAME);
item2.FirmID = elem.FIRM_ID;
item2.FirmName = elem.FIRM_NAME;
item2.ManagerStrategyID = elem.MANAGER_STRATEGY_ID;
item2.ManagerStrategyName = elem.MANAGER_STRATEGY_NAME;
item2.ManagerAccountClassID = elem.MANAGER_ACCOUNTING_CLASS_ID;
item2.ManagerAccountingClassName = elem.MANAGER_ACCOUNTING_CLASS_NAME;
item2.ManagerFundID = elem.MANAGER_FUND_ID;
item2.ManagerFundName = elem.MANAGER_FUND_NAME;
item2.Nav = elem.NAV;
item2.EvalDate = elem.EVAL_DATE.HasValue ? elem.EVAL_DATE.Value.ToString("MMM dd, yyyy") : string.Empty;
item2.ProductID = elem.PRODUCT_ID;
item2.ProductName = elem.PRODUCT_NAME;
item2.UsdEmv = Math.Round((decimal)elem.UsdEmv);
item2.GroupPercent = elem.GroupPercent;
item2.WeightWithEq = elem.WEIGHT_WITH_EQ;
result.Add(item2);
}
}
return result;
}
change it to:
var result = allocationsGrouped
.Where(product => !string.IsNullOrEmpty(product.PRODUCT_NAME))
.GroupBy(product => product.PRODUCT_NAME)
.Select(group => {
var product = group.First();
return new FirmWideAllocationsViewModel()
{
Hierarchy = new List<string>() { manStratName, product.PRODUCT_NAME },
FirmID = product.FIRM_ID,
FirmName = product.Item.FIRM_NAME,
ManagerStrategyID = product.MANAGER_STRATEGY_ID,
ManagerStrategyName = product.MANAGER_STRATEGY_NAME,
ManagerAccountClassID = product.MANAGER_ACCOUNTING_CLASS_ID,
ManagerAccountingClassName = product.MANAGER_ACCOUNTING_CLASS_NAME,
ManagerFundID = product.MANAGER_FUND_ID,
ManagerFundName = product.MANAGER_FUND_NAME,
Nav = product.NAV,
EvalDate = product.EVAL_DATE.HasValue ? product.EVAL_DATE.Value.ToString("MMM dd, yyyy") : string.Empty,
ProductID = product.PRODUCT_ID,
ProductName = product.PRODUCT_NAME,
UsdEmv = Math.Round((decimal)product.UsdEmv),
GroupPercent = product.GroupPercent,
WeightWithEq = product.WEIGHT_WITH_EQ,
//assign aggregate Sum here
EmvSum = group.Sum(x => x.EMV),
};
});

How to union two LINQ queries but the second query need have more fields

I need make union between two LINQ queries, but the second query need have more fields that the first. How can I do it?
Example:
public static void Dummy()
{
var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
var query2 = this.Db.Table2.Select(s => new MyObject() { A = s.Field1, B = s.Field2, C = s.Field3 });
var result = query1.Union(query2);
}
When I calls result.ToList(), occurs the following error:
The type 'MyObject' appears in two structurally incompatible
initializations within a single LINQ to Entities query. A type can be
initialized in two places in the same query, but only if the same
properties are set in both places and those properties are set in the
same order.
How Can I resolve this problem?
Obs.: I can't put the Field3 in the query1 (I don't have access to the query one, because this I Can't changed it)
You don't have to put Field3 in first query but Union requires same number of columns and in same order. Specify a dummy value for third column/field C like:
var query1 = this.Db.Table1.Select(s => new MyObject()
{ A = s.Field1, B = s.Field2 , C= ""});
Assign C whatever is the default value of Field3, may be null for reference type and 0 for numbers etc.
If you don't have access to it modify query1 then create a new query using query1 like:
var newQuery = query1.Select(s=> new MyObject()
{ A = A, B = B , C= ""});
and then use that in Union
var result = newQuery.Union(query2);
As-is, you can't. You can only union 2 sets that have the same structure. If you don't mind modifying query1, however:
var query1 = this.Db.Table1.Select(s => new MyObject()
{ A = s.Field1, B = s.Field2, C = null });
This would allow them to union properly, as they have the same structure.
You can do it, like this:
Create a object devired from MyObject
class MyObjectUnion : MyObject{
}
So, the method goes like this:
public static void Dummy()
{
var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
var query1modified = this.Db.Table2.Select(s => new MyObjectUnion() { A = s.Field1, B = s.Field2, C = null });
var query2 = this.Db.Table2.Select(s => new MyObjectUnion() { A = s.Field1, B = s.Field2, C = s.Field3 });
var result = query1modified.Union(query2);
}
It works
Because records in query1 will never have a property "C", and all records in query2 will have a property "C", it is unlikely that a record in query1 will be equivalent to a record in query2. The only reason for using Union over Concat is to remove duplicates and since you can't have any, you should likely be using Concat instead of Union.
public static void Dummy()
{
var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
var query2 = this.Db.Table2.Select(s => new MyObject() { A = s.Field1, B = s.Field2, C = s.Field3 });
var result = query1.ToList().Concat(query2);
}
There are exceptions, as if you have a custom IEqualityComparer for MyObject that ignores the "C" property, or the default for the "C" property may exist in a record for table2, and you wanted to remove the duplicate, or if there possibly exists duplicates within either query1 or query2 and you wanted them removed then you can still use Concat, but you need to use Distinct before the Concat.
Editted to force query1 to be materialized before concatenation via .ToList()
Double checked with LinqPad, and the following executable had no issues, using a datasource that had both Categories and Cities tables of which were completely different schemas:
void Main()
{
var query1 = Categories.Select(s => new MyObject { A = s.id, B = s.name });
var query2 = Cities.Select(s => new MyObject { A = s.id, B = s.city_name, C = s.location });
var result = query1.ToList().Concat(query2);
result.Dump();
}
public class MyObject
{
public int A {get;set;}
public string B {get;set;}
public object C {get;set;}
}

How to add an order(autoIncrement) property to collection using Linq?

Having:
Initialize an anonymouse collection (I would send it as json)
var myCollection = new[]
{
new
{
Code = 0,
Name = "",
OtherAttribute = ""
}
}.ToList();
myCollection.Clear();
And get the data.
myCollection = (from iPeople in ctx.Person
join iAnotherTable in ctx.OtherTable
on iPeople.Fk equals iAnotherTable.FK
...
order by iPeople.Name ascending
select new
{
Code = iPeople.Code,
Name = iPeople.Name,
OtherAttribute = iAnotherTable.OtherAtribute
}).ToList();
I want to add an Identity column, I need the collection ordered and a counted from 1 to collection.count. Is for binding this counter to a Column in a table (jtable).
var myCollection = new[]
{
new
{
Identity = 0,
Code = 0,
Name = "",
OtherAttribute = ""
}
}.ToList();
myCollection = (from iPeople in ctx.Person
join iAnotherTable in ctx.OtherTable
on iPeople.Fk equals iAnotherTable.FK
...
order by iPeople.Name ascending
select new
{
Identity = Enum.Range(1 to n)//Here I donĀ“t know how to do; in pl/sql would be rownum, but in Linq to SQL how?
Code = iPeople.Code,
Name = iPeople.Name,
OtherAttribute = iAnotherTable.OtherAtribute
}).ToList();
If you are using linq to entities or linq to sql, get your data from the server and ToList() it.
Most likely this answer will not translate to sql but I have not tried it.
List<string> myCollection = new List<string>();
myCollection.Add("hello");
myCollection.Add("world");
var result = myCollection.Select((s, i) => new { Identity = i, Value = s }).ToList();
As Simon suggest in comment, that could would look like below:
int counter = 0; //or 1.
myCollection = (from iPeople in ctx.Person
join iAnotherTable in ctx.OtherTable
on iPeople.Fk equals iAnotherTable.FK
...
order by iPeople.Name ascending
select new
{
Identity = counter++,
Code = iPeople.Code,
Name = iPeople.Name,
OtherAttribute = iAnotherTable.OtherAtribute
}).ToList();
Is there any problem in executing this kind of code?
As Simon stated in his comments, consider the following, albeit contrived, example:
int i = 0;
var collection = Enumerable.Range(0, 10).Select(x => new { Id = ++i });
One solution that helped me to achieve the same goal:
Create a separate Function like this:
private int getMaterialOrder(ref int order)
{
return order++;
}
Then call it in your linq query like:
...
select new MaterialItem() { Order=getMaterialOrder(ref order),
...

Initialise Generic List whilst providing multiple collections? c#

var a = new []{"a"};
var b = new []{"b"};
var c = new []{"c"};
Is it possible to declare / initialise a generic list, providing the three collections above in one line?
var l = new List<string>(a); //fine for one
var l2 = new List<string>(new[] { a, b, c }.SelectMany(x => x)); //this will work but its horrible!
How about:
var list = a.Concat(b).Concat(c).ToList();

Categories