Compare two generic lists and remove duplicates - c#

I have two generic Lists, one called "Featured" and the other called "Filtered".
List<Content> Featured = new List<Content>();
List<Content> Filtered = new List<Content>();
Both contain "Content" items which are simple classes like so :
public class Content
{
public long ContentID { get; set;}
public string Title { get; set; }
public string Url { get; set; }
public string Image { get; set; }
public string Teaser { get; set; }
public Content(long contentId, string title, string url, string image, string teaser)
{
ContentID = contentId;
Title = title;
Url = url;
Image = image;
}
}
Any items that appear in "Filtered" but also appear in "Featured" need to be removed from "Filtered". Additionally, both lists will then be combined into a single generic list with the "Featured" items appearing first.
I know I could write a couple of foreach loops to do this but I can't help feel there must be a more elegant method using LINQ.
I am using C# 4.0.

If you have an IEqualityComparer defined you can use the Union method:
List<Content> FeaturedAndFiltered = Featured.Union(Filtered, new MyContentComparer());
A rough implementation of MyContentComparer would be:
public class ContentEqualityComparer : IEqualityComparer<Content>
{
public bool Equals(Content c1, Content c2)
{
return (c1.ContentID == c2.ContentID);
}
public int GetHashCode(Content c)
{
return c.ContentID.GetHashCode();
}
}

You're looking for the LINQ method Union, specifically
var singleList = Featured.Union(Filtered);
This will return all the Featured items, followed by all the Filtered items that were not Featured. Note that it will also remove any duplicates within a list - so if an item is Featured twice, it will only show up once.
You do, however, need to be able to compare instances of Content in order to do this, either by adding in implementations of Equal and GetHashCode or by providing an IEqualityComparer.

Assuming the objects presenter in both lists are the actual same objects, you can do the following:
var totalList = Featured
.Concat(Filtered.Where(f => !Featured.Contains(f)))
.ToList()
Update
Or, using the Except method as mentioned by Mahmoud Gamal:
var totalList = Featured
.Concat(Filtered.Except(Featured))
.ToList()

Related

Trying to use reflection to concatenate lists of objects

I have below class
public class HydronicEquipment
{
public List<LibraryHydronicEquipment> Source { get; set; }
public List<LibraryHydronicEquipment> Distribution { get; set; }
public List<LibraryHydronicEquipment> Terminals { get; set; }
}
and then i have the below class for "libraryHydronicEquipment"
public class LibraryHydronicEquipment : IEquipmentRedundancy
{
public string Name { get; set; }
public RedundancyStatus RedundancyStatus { get; set; }
public EquipmentRedundancy EquipmentRedundancy { get; set; }
}
I am trying to concatenate the list of "LibraryHydronicEquipment" objects available from all three properties (i.e) from source, distribution and terminal and General concatenate method will looks like as this below
var source = hydronicEquipment.Source;
var distribution = hydronicEquipment.Distribution;
var teriminals = hydronicEquipment.Terminals;
Source.Concat(Distribution).Concat(Terminals)
I am trying to achieve the same using reflection and the code looks like as below
foreach (var (systemName, hydronicEquipment) in hydronicSystemEquipment)
{
bool isFirstSystem = true;
var equipmentList = new List<string> { "Source", "Distribution", "Terminals" };
var redundancyequipmentList = GetRedundancyEquipment(hydronicEquipment, equipmentList);
}
and the method GetRedundancyEquipment is looks like below
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
dynamic equipmentResults = null;
foreach(var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults = equipmentRedundancies.Concat(componentList);
}
return equipmentResults;
}
The problem here is even though i have Source is having list of objects and Distribution is having list of objects, the equipmentResults is giving only one object instead of list of concatenated objects.
I am trying to return the IEnumerable<IEquipmentRedundancy> at the end using reflection method but it seems not working with the above code.
Could any one please let me know how can i achieve this, Many thanks in advance.
GetRedundancyEquipment should preserve your values instead of reassign the reference with each iteration. Here's the fixed version:
private static IEnumerable<IEquipmentRedundancy> GetRedundancyEquipment(HydronicEquipment hydronicEquipment, List<string> equipmentList)
{
IEnumerable<IEquipmentRedundancy> equipmentRedundancies = new List<IEquipmentRedundancy>();
var equipmentResults = new List<IEquipmentRedundancy>();
foreach (var equipment in equipmentList)
{
var componentList = hydronicEquipment.GetType().GetProperty(equipment).GetValue(hydronicEquipment, null) as IEnumerable<IEquipmentRedundancy>;
equipmentResults.AddRange(equipmentRedundancies.Concat(componentList));
}
return equipmentResults;
}
If we look at what you're doing in GetRedundancyEquipment() it becomes clear.
First you create equipmentRedundancies = new List<IEquipmentRedundancy>();
Then you never modify equipmentRedundancies - e.g. via Add(). It remains an empty list until it goes out of scope and is garbage collected.
In a loop you then repeatedly make this assignment equipmentResults = equipmentRedundancies.Concat(componentList);
That is to say: Assign to equipmentResults the concatenation of componentList to equipmentRedundancies.
Note that Concat() is a lazily evaluated linq method. When you actually enumerate it results are produced. It doesn't modify anything, it's more like a description of how to produce a sequence.
So each time through the loop you're assigning a new IEnumerable that describes a concatentaion of an empty list followed by the property that you retrieved with reflection to equipmentResults. Then at the end you return the final one of these concatenations of an empty list and retrieved property.
If you want all of them together, you should concatenate each of them to the result of the previous concatenation, not to an empty list.

Fastest way of comparing two lists without using nested loops

I have two types:
public class SubCategories
{
public static List<SubCategories> subCategories = new List<SubCategories>();
public string title { get; set; }
public string IDfromCategories { get; set; }
public string subCategoryID { get; set; }
public bool isChecked { get; set; }
}
public class UserInsideCategories
{
public string userEmail { get; set; }
public string iDfromSubCategories { get; set; }
}
And two lists both containing this object multiple times.
Now I wanna go through a list with type SubCategories and check each object, if it contains the same value as my other list of type UserInsideCategories. Specifically, I wanna know if any object on the list.SubcategoryID is equal to any object on the other list.IdFromSubCateogires.
I achieved this like so:
List<SubCategories> branch = new List<SubCategories>();
for(int i = 0; i < subCategories.Count; i++)
{
SubCategories e = new SubCategories();
for(int x = 0; x < allSubs.Count; x++)
{
if (e.IDfromCategories == allSubs[x].iDfromSubCategories)
e.isChecked = true;
}
branch.Add(e);
}
So I am using a nested loop. But since I have to do this multiple times, it takes far too long.
I also thought about turning all values from SubCategories into a simple string array and use the Contains function, to see if the current object.IDfromCategories contains the object on the array. This would mean I would NOT use a for loop. But interenally, I believe, the system is still using a loop and therefore there would be no performance benefit.
What would be the best way of checking each object if it contains a value from the other list?
You should use some kind of lookup table. Probably either HashSet or Dictionary. The former only allows checking if a key exists in the set, while the later allows you to also find the object the key belongs to.
To check all the UserInsideCategories that shares an id with a SubCategories you could write:
var dict = subCategoriesList.ToDictionary(s => s.subCategoryID, s => s);
var matches = userInsideCategoriesList.Where(l => dict.ContainsKey(l.iDfromSubCategories));
if you want matching pairs you could write:
foreach (var user in userInsideCategoriesList)
{
if (dict.TryGetValue(user.iDfromSubCategories, out var subCategory))
{
// Handle matched pairs
}
}
This assumes that the ID is unique in respective list. If you have duplicates you would need something like a multi-value dictionary. There are no multi-value dictionary built in, but I would expect there are some implementations if you search around a bit.

Distinct List of object in C#

I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());

How to solve "Expected element name to be '_t', not 'number'."

I have a mongo model like this:
class ObjectA {
[BsonId(IdGenerator = typeof(BsonObjectIdGenerator))]
public BsonObjectId Id;
[BsonElement("number")]
public int Number { get; set; }
[BsonElement("b")]
public List<ObjectB> objectB { get; set; }
}
class ObjectB {
[BsonElement("someProperty")]
public string SomeProperty { get; set; }
}
My problem is when I aggregate the collection with {$unwind:objectB}. The result documencts have a unique object on the property objectB (not a list).
So the cast failes with the exception:
An error occurred while deserializing the ObjectB property of class
ObjectA: Expected element name to be '_t', not
'number'.
Do I have to create a new model for this or is there a easier way to solve it?
You could also choose to work with BsonDocument directly (but that is not strongly typed and more cumbersome to work with), e.g. (I'm using the simple Posts/Tags example here)
var aggregationResults = db.GetCollection("Posts").Aggregate().ResultDocuments;
foreach (var document in aggregationResults)
{
var tag = document.GetValue("Tags").AsString;
}
Unlike the normal query and projection operators, the aggregation framework may change the structure of your document. As you already pointed out, $unwind transforms a document that contains an array into a number of documents that each have a single value of the same name.
Another approach this is to indeed create a new type for this, so
class Post {
public List<string> Tags { get; set; }
...
would become
class PostAggregationResult {
public string Tags { get; set; }
...
That is very easy to work with, but if you have very various aggregation queries, you need a large number of classes which can be annoying.

Linq extracting objects

I have a JSON "multi-level" response that I need to deserialize and from the deserialized classes structure I need to extract all the objects of a certain class.
Below the code I'm using, at the end I find that my result is empty, not populated.
// given these two classes:
[DataContract]
public class ThingsList
{
[DataMember(Name = "status")]
public string Status { get; set; }
[DataMember(Name = "since")]
public double Since { get; set; }
[DataMember(Name = "list")]
public Dictionary<string, ThingsListItem> Items { get; set; }
public DateTime SinceDate { get { return UnixTime.ToDateTime(Since); } }
}
[DataContract]
public class ThingsListItem
{
[DataMember(Name = "url")]
public string Url { get; set; }
[DataMember(Name = "title")]
public string Title { get; set; }
}
// I can deserialize my json to this structure with:
ThingsList results = JsonConvert.DeserializeObject<ThingsList>(e.Result);
// now I need to "extract" only the ThingsListItem objects, and I'm trying this:
var theList = from item in results.Items.OfType<ThingsListItem>()
select new
{
Title = item.Title,
Url = item.Url
};
// but "theList" is not populated.
The points here are (I believe):
- I try to use results.Items.OfType() in order to extract only the ThingsListItem objects, that in the "upper" class are declared in the
public Dictionary Items { get; set; }
row.
Any idea? Tell if it's not clear...
Thanks
Andrea
EDIT: updated my response for clarity.
Since your Dictionary values are of type ThingsListItem you can access them directly by using the Dictionary's Values property. There is no need to use OfType to check their type and extract them. Simply use:
var items = results.Items.Values;
The Values property would return an ICollection<ThingsListItem>. You can then iterate over the results with a foreach. LINQ does not have to be used.
While the Values property described above should be sufficient, I will point out a few issues with your original LINQ query attempt.
1) The following query is probably what you were after. Again, the Dictionary's Values property is key (no pun intended) to accessing the items:
var theList = from item in results.Items.Values
select new
{
Title = item.Title,
Url = item.Url
};
2) Why are you using new? That will return an IEnumerable of anonymous types. You already have a defined class, so why project into a new anonymous type? You should retain the underlying ThingsListItem items by selecting the item directly to get an IEnumerable<ThingsListItem>:
var theList = from item in results.Items.Values
select item;
foreach (var item in theList)
{
Console.WriteLine("Title: {0}, Url: {1}", item.Title, item.Url);
}
You would usually project into a new anonymous type to define a type with data properties you are interested in. Generally you would use them immediately after the query, whereas a selection into an existing class could be used immediately or passed around to other methods that are expecting that type.
Hopefully this has cleared up some questions for you and you have a better idea of using LINQ and when to use the new keyword. To reiterate, for your purposes it seems the Values property should suffice. Using LINQ to select the item is redundant when there are other immediate means to do so.

Categories