I have two lists of entities. Imagine list1 is remote and list2 is local - list1 was created some time in the past and list2 has just been generated.
I want to compare both lists, matching by .id, and comparing the .flag property only of each element. Where the .flag property is different, I want to select the older element, but with the .flag property from list2 (The new list).
The example below shows how to select just the entities in list1 that are different. How can I select the entities from list1 that are different, but using the .flag property from the list2 entity.
Note: I don't want to select new SomeEntity(){} an entire SomeEntity class as in the real problem, the classes I'm working with have a lot of properties.
class SomeEntity
{
public int id;
public bool flag;
public int some_value = -1;
}
// Setup the test
List<SomeEntity> list1 = new List<SomeEntity>();
List<SomeEntity> list2 = new List<SomeEntity>();
for (int i = 0; i < 10; i++ )
{
list1.Add(new SomeEntity() { id = i, flag = true, some_value = i * 100 });
list2.Add(new SomeEntity() { id = i, flag = true, });
}
// Toggle some flags
list1[3].flag = false;
list2[7].flag = false;
// Now find the entities that have changed and need updating
var items_to_update = from x in list1
join y in list2 on x.id equals y.id
where x.flag != y.flag
select x;
You can add this to your code, after retrieving the items_to_update collection:
foreach (var item in items_to_update)
{
item.flag = list2.Where(c => c.id == item.id).SingleOrDefault().flag;
}
How can I select the entities from list1 that are different, but using the .flag property from the list2 entity.
and
I don't want to select new SomeEntity(){}
Means that you want to modify entity in list1 before return. Linq is not clear tool for do that.
foreach (var item in from x in list1
join y in list2 on x.id equals y.id
where x.flag != y.flag
select new {x, y})
{
item.x.flag = item.y.flag
yield return item.x;
}
Related
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
};
I have 2 lists.
They are different in length but same type.
I want that an Item from List2 replaces an equal item in List1.
var item1 = new Item { Id = 1, Name = "Test1" };
var item2 = new Item { Id = 2, Name = "Test2" };
var item3 = new Item { Id = 3, Name = "Test3" };
var item4 = new Item { Id = 4, Name = "Test4" };
var item5 = new Item { Id = 5, Name = "Test5" };
var list1 = new List<Item> { item1, item2, item3, item4, item5 };
var list2 = new List<Item> { new Item { Id = 1, Name = "NewValue" } };
As a result I expect a list with 5 items where the item with Id = 1 has a value "NewValue".
How can I do that preferable with linq.
UPDATE
I extend my question:
How can the replacement of the replaced Item happen without copying all properties manually. Just imagine I have 100 properties...
This is one way to do it:
First define an equality comparer that depends only on the Id property of the Item class like this:
public class IdBasedItemEqualityComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
return x.Id == y.Id;
}
public int GetHashCode(Item obj)
{
return obj.Id.GetHashCode();
}
}
Then you can take items list1 that don't have corresponding items in list2 using the Except method and then you can concatenate that with list2 using the Concat method like this:
var result = list1.Except(list2, new IdBasedItemEqualityComparer()).Concat(list2).ToList();
Notice how I use the IdBasedItemEqualityComparer with the Except method, so that comparison is based only on Id.
Off the top of my head this is one solution
var list3 = new List<Item>();
foreach (var item in list1)
list3.Add(list2.FirstOrDefault(s => s.Id == item.Id) ?? item);
I think LEFT OUTER JOIN in Linq will be able to merge 2 lists regardless of number of properties(columns) like this:
List<Item> newItems =
(from l1 in list1
join l2 in list2 on l1.Id equals l2.Id into l12
from l2 in l12.DefaultIfEmpty()
select new { Item = (l2 == null) ? l1 : l2 }).Select(r => r.Item).ToList();
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),
...
I have retrieved list of my specific class with 150 records.
Now, i want only those records which have Licenseid which are in my another int List.
For example My MainList
List<CustomerDocument> _list = GetListByID(CustomerID);
In this list i have column LicenseID,CustomerID,CustomerName,Type,Age e.t.c
And SecontList
List<int> type = new List<int>();
In Int list i add LicenseID one by one dynamically.
Public class CustomerDocument
{
public int LicenseID{get;set;};
public int CustomerID{get;set;};
public string CustomerName{get;set;};
public int Age{get;set;};
}
This is my CustomerDocument class for which i am getting list.
And now suppose, If Int list has three records , then i want those records from my Main List which have these three LicenseID in my Int List using Linq.
_list = ???
List<CustomerDocument> list = new List<CustomerDocument>();
List<Int> types = new List<Int>();
MapSearchSalesEntities datacontext = new MapSearchSalesEntities();
var collection = ddlLicenseType.CheckedItems;
if (collection.Count > 0)
{
foreach (var item in collection)
{
int value = Convert.ToInt32(item.Value);
types .Add(value);
}
}
var query = (from t1 in datacontext.Licenses
select new CustomerDocument
{
LicenseID = t1.LicenseID,
CustomerID = t1.CustomerID,
CustomerName= t1.CustomerName,
Age= t1.Age,
});
list = query.ToList(); ---gives 150 Records
if (types != null && types.Count > 0)
{
list = list.Where(c => types.Contains(c.LicenseID)).ToList(); --- Gives 0 Records
}
The most efficient approach is to use Enumerable.Join:
var documents = from doc in _list
join licenseID in type
on doc.LicenseID equals licenseID
select doc;
if you want to replace the list:
_list = documents.ToList();
You could also use Enumerable.Where + List.Contains which is not as efficient but shorter:
_list = _list.Where(d => type.Contains(d.LicenseID)).ToList();
Using the LinQ Where method, this is very easy:
_list = _list.Where(c => type.Contains(c.LicenseID)).ToList();
Here is a linq query
var result = (from cust in _list join id in type on cust.LicenseID equals id select cust).ToArray();
I have a list of Filters that are passed into a webservice and I iterate over the collection and do Linq query and then add to the list of products but when I do a GroupBy and Distinct() it doesn't remove the duplicates. I am using a IEnumerable because when you use Disinct it converts it to IEnumerable. If you know how to construct this better and make my function return a type of List<Product> that would be appreciated thanks.
Here is my code in C#:
if (Tab == "All-Items")
{
List<Product> temp = new List<Product>();
List<Product> Products2 = new List<Product>();
foreach (Filter filter in Filters)
{
List<Product> products = (from p in db.Products where p.Discontinued == false
&& p.DepartmentId == qDepartment.Id
join f in db.Filters on p.Id equals f.ProductId
join x in db.ProductImages on p.Id equals x.ProductId
where x.Dimension == "180X180"
&& f.Name == filter.Name /*Filter*/
select new Product
{
Id = p.Id,
Title = p.Title,
ShortDescription = p.ShortDescription,
Brand = p.Brand,
Model = p.Model,
Image = x.Path,
FriendlyUrl = p.FriendlyUrl,
SellPrice = p.SellPrice,
DiscountPercentage = p.DiscountPercentage,
Votes = p.Votes,
TotalRating = p.TotalRating
}).ToList<Product>();
foreach (Product p in products)
{
temp.Add(p);
}
IEnumerable temp2 = temp.GroupBy(x => x.Id).Distinct();
IEnumerator e = temp.GetEnumerator();
while (e.MoveNext()) {
Product c = e.Current as Product;
Products2.Add(c);
}
}
pf.Products = Products2;// return type must be List<Product>
}
You need to override Equals and GetHashCode to compare Products by value.
You're assigning the GroupBy result to temp2, but then you never use it!
IEnumerable temp2 = temp.GroupBy(x => x.Id).Distinct();
IEnumerator e = temp.GetEnumerator(); // <- why temp and not temp2???
But you aren't really using the GroupBy operator properly at all. You need to group and then select an item from each group, not select the distinct groups. The groups are already distinct.
When you group by the Id, you'll actually return a structure like this
-Key: 1
-Product (Id = 1)
-Key: 2
-Product (Id = 2)
-Product (Id = 2)
You need to retrieve an item from each group. Assuming the are duplicates, you won't care which item you get so you can just take the first item from each group.
var groups = temp.GroupBy(x => x.Id);
var distinctItems = groups.Select(g => g.First());
foreach (var item in distinctItems)
{
// do stuff with item
Products2.Add(item);
}
SLaks is right, you have to override the Equals() and GetHashCode() methods to accomplish this.
Here is how I did it within an Order object in small program I wrote. Hope this helps!
//overridden Equals() method from the Object class, determines equality based on order number
public override bool Equals(object obj)
{
bool equal;
if (this.GetType() != obj.GetType())
equal = false;
else
{
Order temp = (Order)obj;
if(OrderNumber == temp.OrderNumber)
equal = true;
else
equal = false;
}
return equal;
}
//overridden GetHashCode() method from Object class, sets the unquie identifier to OrderNumber
public override int GetHashCode()
{
return OrderNumber;
}