Select unique items with LINQ - c#

When I use the following code I get the same items multiple times.
XElement neededFiles = new XElement("needed",
from o in _9nFiles.Elements()
join t in addedToSitePull.Elements()
on o.Value equals
t.Value
where o.Value == t.Value
select new XElement("pic", o.Value));
I'd like to get only unique items. I saw a Stack Overflow post, How can I do SELECT UNIQUE with LINQ?, that used it, and I tried to implement it, but the change had no affect.
The code:
XElement neededFiles = new XElement("needed",
(from o in _9nFiles.Elements()
join t in addedToSitePull.Elements()
on o.Value equals
t.Value
where o.Value == t.Value
select new XElement("pic", o.Value)).Distinct() );

I imagine the reason this doesn't work is because XElement.Equals uses a simple reference equality check rather than comparing the Value properties of the two items. If you want to compare the values, you could change it to:
_9nfiles.Elements()
.Join(addedToSitePull, o => o.Value, t => t.Value, (o, t) => o.Value)
.Distinct()
.Select(val => new XElement("pic", val));
You could also create your own IEqualityComparer<T> for comparing two XElements by their values. Note this assumes all values are non-null:
public class XElementValueEqualityComparer : IEqualityComparer<XElement>
{
public bool Equals(XElement x, XElement y)
{
return x.Value.Equals(y.Value);
}
public int GetHashCode(XElement x)
{
return x.Value.GetHashCode();
}
}
Then you could replace the existing call to Distinct with Distinct(new XElementValueEqualityComparer()).

Distinct doesn't work because XElements are compared by reference, not by value.
The solution is to use another overload of Distinct - Distinct(IEqualityComparer);
You need to implement IEqualityComparer for example as follows:
class XElementEqualityComparer : IEqualityComparer<XElement>
{
#region IEqualityComparer<XElement> Members
public bool Equals(XElement x, XElement y)
{
if (x == null ^ y == null)
return false;
if (x == null && y == null)
return true;
return x.Value == y.Value;
}
public int GetHashCode(XElement obj)
{
if (obj == null)
return 0;
return obj.Value.GetHashCode();
}
#endregion
}

It's not a good solution - but really easy.
foreach (XElement pic in neededFiles.Elements())
{
unSyncedPictures.Add(pic.Value);
}
List<string> temp = new List<string>();
temp.AddRange(unSyncedPictures.Distinct());

Related

Intersect between 2 collections in C#

I have:
List<INFRAESTRUCTURA> l1 = listQ1.ToList();
List<INFRAESTRUCTURA> l2 = listQ2.ToList();
And I need to intersect it comparing ids. Something like that:
l1.Intersect(l2, l1[].id_infraestructura == l2[].id_infraestructura)
But I don't know which method I must use and it sintax.
I found this:
var ids = list1.Select(a => a.id).Intersect(list2.Select(b => b.id));
But this return a list of ids and i need a list of elements contained in both lists.
Thank you!
I would use Enumerable.Join:
var intersecting = from i1 in l1
join i2 in l2
on i1.id_infraestructura equals i2.id_infraestructura
select i1;
List<INFRAESTRUCTURA> result = intersecting.ToList();
If you would override Equals + GetHashCode in INFRAESTRUCTURA or provide a custom IEqualityComparer<INFRAESTRUCTURA> you could use Enumerable.Intersect directly:
List<INFRAESTRUCTURA> result = l1.Intersect(l2).ToList();
Here's a possible implementation:
public class InfrastructureComparer : IEqualityComparer<INFRAESTRUCTURA>
{
public bool Equals(INFRAESTRUCTURA x, INFRAESTRUCTURA y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.id_infraestructura == y.id_infraestructura;
}
public int GetHashCode(INFRAESTRUCTURA obj)
{
if (obj == null) return 0;
return obj.id_infraestructura;
}
}
you can use the overloads which take an IEqualityComparer<T> like here:
List<INFRAESTRUCTURA> result = l1.Intersect(l2, new InfrastructureComparer()).ToList();
If you want both objects in the result you could use an anonymous type:
var intersecting = from i1 in l1
join i2 in l2
on i1.id_infraestructura equals i2.id_infraestructura
select new { i1, i2 };
The other answers are correct, but you can use Intersect with your custom comparer. You can create custom comparer by implementing IEqualityComparer<> interface. And for implementing this interface we must implmenet two methods, Equals and GetHashCode.
public class InfraestructuraComparer: IEqualityComparer<INFRAESTRUCTURA>
{
/// <summary>
/// Whether the two INFRAESTRUCTURA are equal.
/// </summary>
public bool Equals(INFRAESTRUCTURA firstObj, INFRAESTRUCTURA secondObj)
{
if (firstObj == null && secondObj == null)
return true;
if (firstObj == null || secondObj == null)
return false;
// Your equality logic goes to here
return firstObj.ID == secondObj.ID;
}
/// <summary>
/// Return the hash code for this instance.
/// </summary>
public int GetHashCode(INFRAESTRUCTURA obj)
{
// Don't compute hash code on null object.
if (obj == null) return 0;
unchecked
{
var hash = 17;
hash = hash * 23 + obj.Id.GetHashCode();
return hash;
}
}
}
And then:
var result = list1.Intersect(list2, new InfraestructuraComparer());
You can also use this comparer in Except method, for finding the difference of two sequences.
var result = list1.Except(list2, new InfraestructuraComparer());
Additionally:
From the first point of view you may misunderstood GetHashCode(). You can read about this method in many question of StackOverflow. You can read the answer to this question.
You can use Linq Join
l1.Join(l2, l => l.id_infraestructura, r => r.id_infraestructura, (l,r) => l.id_infraestructura);

Remove duplicate rows from two dimensional list

I have a two-dimensional list of strings (List<List<string>>).
Is there an easy way to remove the duplicate rows? That is the List<string> that are equal.
Build a custom IEqualityComparer based on SequenceEqual :
class ListComparer : IEqualityComparer<List<string>>
{
public bool Equals(List<string> x, List<string> y)
{
if (x == y)
return true ;
if (x == null || y == null)
return false ;
// Order if you need
return x.SequenceEqual(y) ;
}
public int GetHashCode(List<string> obj)
{
if (obj == null)
return 0;
unchecked
{
return obj.Select(e => e.GetHashCode()).Aggregate(17, (a, b) => 23 * a + b);
}
}
}
Apply Distinct() with the comparer :
List<List<string>> original = ...
var sortedListOfList = original.Distinct(new ListComparer()).ToList() ;
You did not specify if the lists should be compared with or without ordering.
Without ordering it should be:
List<List<string>> source = *yourLists*;
var sortedList = source.Distinct();

Convert Object to List

I want convert object to list of myclass, which object is return from linq query.
object list = detailManager.GetMutabakatDetailListByMutabakat(oMutabakat, true);
List<CurrentAccount> accountList = ??
GetMutabakatDetailListByMutabakat method like this;
public object GetMutabakatDetailListByMutabakat(Mutabakat mutabakat, bool Gonderen)
{
var detayIdList = this.Context.MutabakatDetay.Where(s => s.MutabakatId == mutabakat.MutabakatId).Select(s => s.MutabakatDetayId).ToList();
var CariEkstreList =
(from ekstre in this.Context.CariHesapEkstre
join detay in this.Context.MutabakatDetay on ekstre.MutabakatDetayId equals detay.MutabakatDetayId
where detayIdList.Contains(ekstre.MutabakatDetayId.Value) && ekstre.GonderenMukellefFirmaId == mutabakat.GonderenMukellefFirmaId
select new
{
MutabakatDetayId = ekstre.MutabakatDetayId,
MutabakatVar = ekstre.MutabakatVar,
AlanFirmaId = ekstre.AlanFirmaId,
GonderenMukellefFirmaId = ekstre.GonderenMukellefFirmaId,
KayitTarihi = ekstre.KayitTarihi,
DonemYil = ekstre.DonemYil,
DonemAy = ekstre.DonemAy,
Degistirildi = ekstre.Degistirildi,
CariHesapEkstreId = ekstre.CariHesapEkstreId,
AktaranKullaniciId = ekstre.AktaranKullaniciId,
AktarimId = ekstre.AktarimId,
AktarimTarihi = ekstre.AktarimTarihi,
BakiyeTur = ekstre.BakiyeTur,
BelgeNo = ekstre.BelgeNo,
BelgeTarihi = ekstre.BelgeTarihi,
BelgeTur = ekstre.BelgeTur,
IslemTarihi = ekstre.IslemTarihi,
ParaBirimi = ekstre.ParaBirimi,
TLTutar = ekstre.BakiyeTur == "B" ? ekstre.TLTutar * -1 : ekstre.TLTutar,
Tutar = ekstre.BakiyeTur == "B" ? ekstre.Tutar * -1 : ekstre.Tutar
}).ToList();
return CariEkstreList;
}
It depends on what list actually is:
A) if detailManager.GetMutabakatDetailListByMutabakat(oMutabakat, true) returns IEnumerable<CurrentAccount> then all you have to do is to add .ToList():
List<CurrentAccount> accountList = detailManager
.GetMutabakatDetailListByMutabakat(oMutabakat, true)
.ToList();
B) if detailManager.GetMutabakatDetailListByMutabakat(oMutabakat, true) returns IEnumerable<SomeObject> and SomeObject can be cast to CurrentAccount then
List<CurrentAccount> accountList = detailManager
.GetMutabakatDetailListByMutabakat(oMutabakat, true)
.OfType<CurrentAccount>()
.ToList();
C) Finally, in the general case you have to implement .Select:
List<CurrentAccount> accountList = detailManager
.GetMutabakatDetailListByMutabakat(oMutabakat, true)
.Select(item => GetAccountFromItem(item)) //TODO: implement Select
.ToList();
Thanks everyone, i solved my problem with using reflaction.
firstly, object casted to IList
List<CariHesapEkstre> senderExtractList = GetExtractList((IList)detayManager.GetMutabakatDetayListByMutabakat(oMutabakat, true));
private List<CariHesapEkstre> GetExtractList ( IList tempList )
{
List<CariHesapEkstre> returnList = new List<CariHesapEkstre>();
foreach ( var item in tempList )
{
CariHesapEkstre extract = new CariHesapEkstre();
foreach ( PropertyInfo prop in item.GetType().GetProperties() )
{
foreach ( PropertyInfo prop2 in extract.GetType().GetProperties() )
{
if ( prop2.Name == prop.Name )
{
prop2.SetValue(extract, prop.GetValue(item));
}
}
}
returnList.Add(extract);
}
return returnList;
}
There are few things I can say,
What does your method GetMutabakatDetailListByMutabakat return? Does it have to be object?
As its name implies - if a methods name is Get...List I would expect it to return a list, not an object. If you can, first change that behaviour then you'll be ok.
If that method is written by someone else(that you cannot change the behaviour) then you should play with casting operations as the others suggest, but you can only do this if the underlying object is really of type List<CurrentAccount>.
And if the underlying object is not even of type List<CurrentAccount> then you should learn what the object structure is(another class, anonymous object, or dynamic) then we can work something out.
After Update
With the update to your question I can see that you are returning a list of anonymous objects. But isn't it actually CurrentAccount?
So if you select it as following:
public List<CariHesapEkstre> GetMutabakatDetailListByMutabakat ( Mutabakat mutabakat, bool Gonderen )
{
var detayIdList = this.Context.MutabakatDetay.Where(s => s.MutabakatId == mutabakat.MutabakatId).Select(s => s.MutabakatDetayId).ToList();
var CariEkstreList =
( from ekstre in this.Context.CariHesapEkstre
join detay in this.Context.MutabakatDetay on ekstre.MutabakatDetayId equals detay.MutabakatDetayId
where detayIdList.Contains(ekstre.MutabakatDetayId.Value) && ekstre.GonderenMukellefFirmaId == mutabakat.GonderenMukellefFirmaId
/// here you only need to use object initializer for CariHesapEkstre like just below
select new CariHesapEkstre
{
MutabakatDetayId = ekstre.MutabakatDetayId,
MutabakatVar = ekstre.MutabakatVar,
...
Tutar = ekstre.BakiyeTur == "B" ? ekstre.Tutar * -1 : ekstre.Tutar
} ).ToList();
return CariEkstreList;
}
Last Update
I see what you are trying to do, you want a method to do the hardwork for you. So you can check this tool called automapper. It does what you are trying to do. But still it's a hard work for your code too.
But if you are only trying to convert an object to a list you can use the code below.
public List<CariHesapEkstre> ConvertToDesiredType ( object list )
{
return ( (IEnumerable<dynamic>)list ).Select(item => new CariHesapEkstre
{
MutabakatDetayId = item.MutabakatDetayId,
MutabakatVar = item.MutabakatVar,
...
}).ToList();
}

How to getting distinct values by linq or lambda?

I have a list of items, and i try to getting unique items by distinct keys.
The class:
class TempClass
{
public string One { get; set; }
public string Two { get; set; }
public string Key
{
get
{
return "Key_" + One + "_" + Two;
}
}
}
I build the dummy list as follows:
List<TempClass> l = new List<TempClass>()
{
new TempClass(){ One="Da" , Two = "Mi"},
new TempClass(){ One="Da" , Two = "Mi"},
new TempClass(){ One="Da" , Two = "Mi"},
new TempClass(){ One="Mi" , Two = "Da"},
new TempClass(){ One="Mi" , Two = "Da"},
};
My question is - how get only 1 item? by check that does exist only unique key? unique item means that should to check that have there only one key that equals to "Key_Da_Mi" or "Key_Mi_Da"?
how to achieve that?
Group each of the items on a HashSet of strings containing both keys, use HashSet's set comparer to compare the items as sets (sets are unordered) and then pull out the first (or whichever) item from each group:
var distinct = l.GroupBy(item => new HashSet<string>() { item.One, item.Two },
HashSet<string>.CreateSetComparer())
.Select(group => group.First());
You should either implement equality comparison, or implement IEqualityComparer<T> with your specific logic:
class TempClassEqualityComparer : IEqualityComparer<TempClass>
{
public bool Equals(TempClass x, TempClass y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
// For comparison check both combinations
return (x.One == y.One && x.Two == y.Two) || (x.One == y.Two && x.Two == y.One);
}
public int GetHashCode(TempClass x)
{
if (Object.ReferenceEquals(x, null)) return 0;
return x.One.GetHashCode() ^ x.Two.GetHashCode();
}
}
Then you can use this comparer in Distinct method:
var result = l.Distinct(new TempClassEqualityComparer());
Just order them before you create the key.
public string Key
{
get{
List<string> l = new List<string>{One, Two};
l = l.OrderBy(x => x).ToList();
return "Key_" + string.Join("_", l);
}
}

Inserting value in List of values of a Key in Dictionary

I have a rowsDictionary that its keys point to a list of EmployeeSummary classes.
In those EmployeeSummary classes we also have a string property of Delivery_System
I am looping through this in this way but now stuck in the part that I want to have a deliverySystemFinder dictioanry that its keys are combinedKey as below and the value for each key is a list of distinct delivery_system values
//rowsDictionary is a Dictionary<string, List<EmployeeSummary>>
Dictionary<string, List<string>> deliverySystemFinder = new Dictionary<string, List<string>>();
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() + emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
// so now I should go and
//A) does deliverySystemFinder have this combinedKey? if not add it.
//B) Does combinedKey in the list of its values already have the value for delivery_system? if it does not then add it
}
}
This would work, for start:
foreach (string key in rowsDictionary.Keys)
{
List<EmployeeSummary> empList = rowsDictionary[key];
foreach (EmployeeSummary emp in empList)
{
string combinedKey = emp.LastName.Trim().ToUpper() +
emp.FirstName.Trim().ToUpper();
string delivery_system = emp.Delivery_System;
List<string> systems = null;
// check if the dictionary contains the list
if (!deliverySystemFinder.TryGetValue(combinedKey, out systems))
{
// if not, create it and add it
systems = new List<string>();
deliverySystemFinder[combinedKey] = systems;
}
// check if the list contains the value and add it
if (!systems.Contains(delivery_system))
systems.Add(delivery_system);
}
}
Now, a couple of remarks:
It doesn't make sense to iterate through Keys, and then do a lookup in each iteration. You can directly iterate KeyValuePairs using a foreach loop.
Using concatenated strings as unique keys often fails. In this case, what happens if you have users { LastName="Some", FirstName="Body" } and { LastName="So", FirstName="Mebody" } in your list?
Checking if a List contains a value is a O(n) operation. You would greatly improve performance if you used a HashSet<string> instead.
Finally, the simplest way to achieve what you're trying to do is to ditch those loops and simply use:
// returns a Dictionary<EmployeeSummary, List<string>>
// which maps each distinct EmployeeSummary into a list of
// distinct delivery systems
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s, new EmployeeSummaryEqualityComparer())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());
With EmployeeSummaryEqualityComparer defined something like:
class EmployeeSummaryEqualityComparer : IEqualityComparer<EmployeeSummary>
{
public bool Equals(EmployeeSummary x, EmployeeSummary y)
{
if (object.ReferenceEquals(x, null))
return object.ReferenceEquals(y, null);
return
x.FirstName == y.FirstName &&
x.LastName == y.LastName &&
... (depending on what constitutes 'equal' for you)
}
public int GetHashCode(EmployeeSummary x)
{
unchecked
{
var h = 31; // null checks might not be necessary?
h = h * 7 + (x.FirstName != null ? x.FirstName.GetHashCode() : 0);
h = h * 7 + (x.LastName != null ? x.LastName.GetHashCode() : 0);
... other properties similarly ...
return h;
}
}
}
If you really think that using the string key will work in all your cases, you can do it without the custom equality comparer:
// returns a Dictionary<string, List<string>>
var groupByEmployee = rowsDictionary
.SelectMany(kvp => kvp.Value)
.GroupBy(s => s.LastName.ToUpper() + s.FirstName.ToUpper())
.ToDictionary(
s => s.Key,
s => s.Select(x => x.Delivery_System).Distinct().ToList());

Categories