Only Add Unique Item To List - c#

I'm adding remote devices to a list as they announce themselves across the network. I only want to add the device to the list if it hasn't previously been added.
The announcements are coming across an async socket listener so the code to add a device can be run on multiple threads. I'm not sure what I'm doing wrong but no mater what I try I end up with duplications. Here is what I currently have.....
lock (_remoteDevicesLock)
{
RemoteDevice rDevice = (from d in _remoteDevices
where d.UUID.Trim().Equals(notifyMessage.UUID.Trim(), StringComparison.OrdinalIgnoreCase)
select d).FirstOrDefault();
if (rDevice != null)
{
//Update Device.....
}
else
{
//Create A New Remote Device
rDevice = new RemoteDevice(notifyMessage.UUID);
_remoteDevices.Add(rDevice);
}
}

If your requirements are to have no duplicates, you should be using a HashSet.
HashSet.Add will return false when the item already exists (if that even matters to you).
You can use the constructor that #pstrjds links to below (or here) to define the equality operator or you'll need to implement the equality methods in RemoteDevice (GetHashCode & Equals).

//HashSet allows only the unique values to the list
HashSet<int> uniqueList = new HashSet<int>();
var a = uniqueList.Add(1);
var b = uniqueList.Add(2);
var c = uniqueList.Add(3);
var d = uniqueList.Add(2); // should not be added to the list but will not crash the app
//Dictionary allows only the unique Keys to the list, Values can be repeated
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"Happy");
dict.Add(2, "Smile");
dict.Add(3, "Happy");
dict.Add(2, "Sad"); // should be failed // Run time error "An item with the same key has already been added." App will crash
//Dictionary allows only the unique Keys to the list, Values can be repeated
Dictionary<string, int> dictRev = new Dictionary<string, int>();
dictRev.Add("Happy", 1);
dictRev.Add("Smile", 2);
dictRev.Add("Happy", 3); // should be failed // Run time error "An item with the same key has already been added." App will crash
dictRev.Add("Sad", 2);

Just like the accepted answer says a HashSet doesn't have an order. If order is important you can continue to use a List and check if it contains the item before you add it.
if (_remoteDevices.Contains(rDevice))
_remoteDevices.Add(rDevice);
Performing List.Contains() on a custom class/object requires implementing IEquatable<T> on the custom class or overriding the Equals. It's a good idea to also implement GetHashCode in the class as well. This is per the documentation at https://msdn.microsoft.com/en-us/library/ms224763.aspx
public class RemoteDevice: IEquatable<RemoteDevice>
{
private readonly int id;
public RemoteDevice(int uuid)
{
id = id
}
public int GetId
{
get { return id; }
}
// ...
public bool Equals(RemoteDevice other)
{
if (this.GetId == other.GetId)
return true;
else
return false;
}
public override int GetHashCode()
{
return id;
}
}

Related

Find matching KVP from Dictionary<List<enum>,string> where search key is List<enum> and return reverse partial matches

I have a Dictionary where the key is a list of enum values, and the value is a simple string.
What I need to do is using another list of enum values find the match KVP.
The curveball and reason for posting here is I also need it to return KVP if the list from my test or search list contains all the items (or enum objects) in any key in the dictionary.
example excerpt of code:
public enum fruit{ apple , orange , banana , grapes };
public class MyClass
{
public Dictionary<List<fruit>, string> FruitBaskets = new Dictionary<List<fruit>, string>;
FruitBaskets.Add(new List<fruit>{apple,orange},"Basket 1");
List<fruit> SearchList = new List<fruit>{orange,apple,grapes};
}
I need to search the dictionary for SearchList and return "Basket 1".
Note that the matching may be backwards than what you would expect for such an example as I need the key to match agains the search list and not vice versa, so extra items in the search list that are not in the key are ok.
I know I could simply iterate the dict and check one by one but I also need this to be as fast as possible as it resides in a loop that is running fairly fast.
What I am currently using is;
public Dictionary<List<fruit>, string> SearchResults;
foreach (KeyValuePair<List<fruit>, string> FruitBasket in FruitBaskets)
{
if (FruitBasket.Key.Except(SearchList).Count() == 0)
SearchResults.Add(FruitBasket);
}
Wondering if there is a better/faster way.
You need to rethink about you choice of Keys in dictionary. There are some major problem with List keys, such as:
You can't use O(1) key lookup with List
Your keys aren't immutable
You can have identical lists as keys without receiving errors, for example you can have:
var a = new[] { fruit.organge }.ToList();
var b = new[] { fruit.organge }.ToList();
fruitBasket.Add(a, "1");
fruitBasket.Add(b, "2");
But is this dictionary valid? I guess not but it depends on your requirements.
You can change Dictionary keys!
For this reasons, you need to change your dictionary key type. You can use combined Enum values instead of using a List with bitwise operators. For this to work, you need to assign powers of 2 to each enum value:
[Flags]
public Enum Fruit
{
Orange = 1,
Apple = 2,
Banana = 4,
Grape = 8
}
You have to combine these enum values to get the desired multi-value enum dictionary key effect:
For [Fruit.Orange, Fruit.Apple] you use Fruit.Orange | Fruit.Apple.
Here's a sample code for combining and decomposing values:
private static fruit GetKey(IEnumerable<fruit> fruits)
{
return fruits.Aggregate((x, y) => x |= y);
}
private static IEnumerable<fruit> GetFruits(fruit combo)
{
return Enum.GetValues(typeof(fruit)).Cast<int>().Where(x => ((int)combo & x) > 0).Cast<fruit>();
}
Now you need a function to get all combinaions (power set) of the SearchList:
private static IEnumerable<fruit> GetCombinations(IEnumerable<fruit> fruits)
{
return Enumerable.Range(0, 1 << fruits.Count())
.Select(mask => fruits.Where((x, i) => (mask & (1 << i)) > 0))
.Where(x=>x.Any())
.Select(x=> GetKey(x));
}
Using these combinations, you can lookup values from dictionary using O(1) time.
var fruitBaskets = new Dictionary<fruit, string>();
fruitBaskets.Add(GetKey(new List<fruit> { fruit.apple, fruit.orange }), "Basket 1");
List<fruit> SearchList = new List<fruit> { fruit.orange, fruit.apple, fruit.grapes };
foreach (var f in GetCombinations(SearchList))
{
if (fruitBaskets.ContainsKey(f))
Console.WriteLine(fruitBaskets[f]);
}
Consider storing your data in a different way:
var FruitBaskets = Dictionary<fruit, List<string>>();
Each entry contains elements that match at least one fruit. Conversion from your structure is as follows:
foreach (var kvp in WobblesFruitBaskets)
{
foreach (var f in kvp.Key)
{
List<string> value;
if (!FruitBaskets.TryGetValue(f, out value))
{
value = new List<string>();
FruitBaskets.Add(f, value);
}
value.Add(kvp.Value);
}
}
Now, the search would look like this: For a composed key searchList you first calculate results for single keys:
var partialResults = new Dictionary<fruit, List<string>>();
foreach (var key in searchList)
{
List<string> r;
if (FruitBaskets.TryGetValue(key, out r))
{
partialResults.Add(key, r);
}
}
Now, what is left is to compose all possible search results. This is the hardest part, which I believe is inherent to your approach: for a key with n elements you have 2n - 1 possible subkeys. You can use one of subset generating approaches from answers to this question and generate your final result:
var finalResults = new Dictionary<List<fruit>, List<string>>();
foreach (var subkey in GetAllSubsetsOf(searchList))
{
if (!subkey.Any())
{
continue; //I assume you don't want results for an empty key (hence "-1" above)
}
var conjunction = new HashSet<string>(partialResults[subkey.First()]);
foreach (var e in subkey.Skip(1))
{
conjunction.IntersectWith(partialResults[e]);
}
finalResults.Add(subkey, conjunction.ToList());
}
I've changed string to List<string> in result's value part. If there is some invariant in your approach that guarantees there will be always only one result, then it should be easy to fix that.
if you create a Dictionary from a Reference Type, you stored just the Reference (Not value), then you can't use simply FruitBaskets[XXX] (except you use the same key that you create the node of dictionary), you must iterate whole of Keys in your dictionary.
I think this function is easy and good for you:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
if (Enumerable.SequenceEqual<fruit>(KEY,item))
return true;
}
return false;
}
and this,
bool B = Contain(new List<fruit> { fruit.apple, fruit.orange }); //this is True
But if you want to consider the permutation of members, you can use this function:
bool Contain(List<fruit> KEY)
{
foreach (var item in FruitBaskets.Keys)
{
HashSet<fruit> Hkey= new HashSet<fruit>(KEY);
if (Hkey.SetEquals(item))
return true;
}
return false;
}
and here's the output:
bool B1 = Contain(new List<fruit> { fruit.orange, fruit.grapes }); // = False
bool B2 = Contain(new List<fruit> { fruit.orange, fruit.apple }); // = True
bool B3 = Contain(new List<fruit> { fruit.apple, fruit.orange }); // = True

Check if an item exist in the dictionary and remove it from the dictionary in C# [duplicate]

This question already has answers here:
Remove Item in Dictionary based on Value
(6 answers)
Closed 9 years ago.
The question should be clear from the title itself. I need to check if an item exist in the dictionary and remove it from the dictionary in C#. The only catch is that i have to do this using only the value item and not the key.
The declaration is as below:
IDictionary<string, myCustomClassObject> clients = new IDictionary<string, myCustomClassObject>();
Now i fill in the dictionary by:
clients["key"] = myCustomClassObject1;
Now how can i find and remove this item myCustomClassObject1 from my Dictionary. I only want to use the value item and not the key
Is this doabale...if so please guide...
regards
Edit: Thank you all....got valuable comments...probably have some thinking to do ...thanks
It depends on how you need it to perform. If you can accept O(N) performance, you could just do something like:
foreach(var pair in clients) {
if(pair.Value == expected) {
clients.Remove(pair.Key);
break;
}
}
However, if you need faster you would need two dictionaries - one the reverse of the other (i.e. keyed by the instances). So when adding, you would do:
clientsByKey.Add(key, value);
clientsByValue.Add(value, key);
so you can do (to remove-by-value):
string key;
if(clientsByValue.TryGetValue(value, out key)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
or similarly (to remove-by-key):
Foo value;
if(clientsByKey.TryGetValue(key, out value)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
It's not very efficient to search a dictionary by it's values. However, you can use Linq to find all entries with a given value.
IEnumerable<KeyValuePair<string, myCustomClassObject>> pairs = clients
.Where(entry => entry.Value.Equals(myCustomClassObject1)).ToList();
foreach (KeyValuePair<string, myCustomClassObject> kv in pairs)
clients.Remove(kv.Key);
This should do it. It removes all clients having a given value.
while (clients.ContainsValue(myCustomClassObject1))
clients.Remove(clients.Where(x => x.Value == myCustomClassObject1).FirstOrDefault().Key);
Or create a new dictionary without the values you want removed
clients = clients.Where(x => x.Value != myCustomClassObject1).ToDictionary(k => k.Key, v => v.Value);
If the collection only contains one item with the value to be removed then you can use one of the other answers here, which will work just fine.
However, if your collection can have multiple items with the same value then you need to be careful.
You cannot modify a collection while iterating over it, so you will need to find the keys of all the items that you want to remove in one loop and put them in a list, and then iterate over that list in a separate loop to delete the items.
For example:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
void run()
{
var dict = new Dictionary<string, int>
{
{"Key1", 1},
{"Key2", 2},
{"Key3", 3},
{"Key4", 2},
{"Key5", 4}
};
int valueToRemove = 2;
var keysToRemove = (from element in dict
where element.Value == valueToRemove
select element.Key).ToList();
foreach (var key in keysToRemove)
dict.Remove(key);
foreach (var element in dict)
Console.WriteLine("Key = {0}, Value = {1}", element.Key, element.Value);
}
static void Main(string[] args)
{
new Program().run();
}
}
}
Use,
Following will remove only first matching value
client newClient = new client();
foreach(KeyValuePair<string, client> client in clients) {
if(client.value.equals(newClient)) {
clients.remove(client.key);
break;
}
}
Or if you want to remove all matching values,
foreach(var client in clients.Where(kvp => kvp.Value == newClient).ToList()) {
clients.Remove(client.Key);
}

Using C# Dictionary to parse log file

I am trying to parse a rather long log file and creating a better more manageable listing of issues.
I am able to read and parse out the individual log line by line, but what I need to do is display only unique entries, as some errors occur more often than others and are always recorded with identical text.
What I was going to try to do was create a Dictionary object to hold each unique entry and as I work through the log file, search the Dictionary object to see if the same values are already in there.
Here is a crude sample of the code I have (a work in progress, I hope I have all syntax right) that does not work. For some reason this script never sees any distinct entries (if statement never passes):
string[] rowdta = new string[4];
Dictionary<string[], int> dict = new Dictionary<string[], int>();
int ctr = -1;
if (linectr == 1)
{
ctr++;
dict.Add(rowdta, ctr);
}
else
{
foreach (KeyValuePair<string[], int> pair in dict)
{
if ((pair.Key[1] != rowdta[1]) || (pair.Key[2] != rowdta[2])| (pair.Key[3] != rowdta[3]))
{
ctr++;
dict.Add(rowdta, ctr);
}
}
}
Some sample data:
First line
rowdta[0]="ErrorType";
rowdta[1]="Undefined offset: 0";
rowdta[2]="/url/routesDisplay2.svc.php";
rowdta[3]="Line Number 5";
2nd line
rowdta[0]="ErrorType";
rowdta[1]="Undefined offset: 0";
rowdta[2]="/url/routesDisplay2.svc.php";
rowdta[3]="Line Number 5";
3rd line
rowdta[0]="ErrorType";
rowdta[1]="Undefined variable: fvmsg";
rowdta[2]="/url/processes.svc.php";
rowdta[3]="Line Number 787";
So, with this, the Dictionary will have 2 items in it, first line and 3rd line.
I have also tried this with the following which nalso does not find any variations in the log file text.
if (!dict.ContainsKey(rowdta)) {}
Can someone please help me get this syntax right? I am just a newbie at C# but this should be relatively straightforward. As always, I am thinking that this should be enough information to get the conversation started. If you want/need more detail, please let me know.
Either create a wrapper for your strings which implements IEquatable.
public class LogFileEntry :IEquatable<LogFileEntry>
{
private readonly string[] _rows;
public LogFileEntry(string[] rows)
{
_rows = rows;
}
public override int GetHashCode()
{
return
_rows[0].GetHashCode() << 3 |
_rows[2].GetHashCode() << 2 |
_rows[1].GetHashCode() << 1 |
_rows[0].GetHashCode();
}
#region Implementation of IEquatable<LogFileEntry>
public override bool Equals(Object obj)
{
if (obj == null)
return base.Equals(obj);
return Equals(obj as LogFileEntry);
}
public bool Equals(LogFileEntry other)
{
if(other == null)
return false;
return _rows.SequenceEqual(other._rows);
}
#endregion
}
Then use that in your dictionary:
var d = new Dictionary<LogFileEntry, int>();
var entry = new LogFileEntry(rows);
if( d.ContainsKey(entry) )
{
d[entry] ++;
}
else
{
d[entry] = 1;
}
Or create a custom comparer similar to that proposed by #dasblinkenlight and use as follows
public class LogFileEntry
{
}
public class LogFileEntryComparer : IEqualityComparer<LogFileEntry>{ ... }
var d = new Dictionary<LogFileEntry, int>(new LogFileEntryComparer());
var entry = new LogFileEntry(rows);
if( d.ContainsKey(entry) )
{
d[entry] ++;
}
else
{
d[entry] = 1;
}
The reason that you see the problem is that an array of strings cannot be used as a key in a dictionary without supplying a custom IEqualityComparer<string[]> or writing a wrapper around it.
EDIT Here is a quick and dirty implementation of a custom comparer:
private class ArrayEq<T> : IEqualityComparer<T[]> {
public bool Equals(T[] x, T[] y) {
return x.SequenceEqual(y);
}
public int GetHashCode(T[] obj) {
return obj.Sum(o => o.GetHashCode());
}
}
Here is how you can use it:
var dd = new Dictionary<string[], int>(new ArrayEq<string>());
dd[new[] { "a", "b" }] = 0;
dd[new[] { "a", "b" }]++;
dd[new[] { "a", "b" }]++;
Console.WriteLine(dd[new[] { "a", "b" }]);
The problem is that array equality is reference equality. In other words, it does not depend on the values stored in the array, it depends only on the identity of the array.
Some solutions
use Tuple to hold the row data
use an anonymous type to hold the row data
create a custom type to hold the row data, and, if it is a class, override Equals and GetHashCode.
create a custom implementation of IEqualityComparer to compare the arrays according to their values, and pass that to the dictionary when you create it.

Value lookup using key or vice versa

First of all, apologies for the nasty title. I will correct it later.
I have some data like below,
"BOULEVARD","BOUL","BOULV", "BLVD"
I need a data structure that is O(1) for looking up any of this words by other. For example, if I use a dictionary I would need to store this keys/values like this, which looks odd to me,
abbr.Add("BLVD", new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" });
abbr.Add("BOUL", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULV", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULEVARD", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
Which data structure to use to keep this data appropriate to my querying terms?
Thanks in advance
Create two HashMap - one maps word to a group number. And the other one maps group number to a list of words. This way you save some memory.
Map<String, Integer> - Word to Group Number
Map<Integer, List<String>> - Group Number to a list of words
You need two O(1) lookups - first to get the group number and then by it - get the list of words.
Assuming abbr is a Dictionary<String, IEnumerable<String>>, you could use the following function:
public static void IndexAbbreviations(IEnumerable<String> abbreviations) {
for (var a in abbreviations)
abbr.Add(a, abbreviations);
}
This will populate the dictionary with the provided list of abbreviations such that when any of them is looked up in the dictionary. It is slightly better than the example code you provided, because I am not creating a new object for each value.
From the documentation, "Retrieving a value by using its key is very fast, close to O(1), because the Dictionary(Of TKey, TValue) class is implemented as a hash table."
The choice of dictionary looks fine to me. As mentioned above, you should use the same list to be referenced in the dictionary. The code could go something like this:
var allAbrList = new List<List<string>>
{
new List<string> {"BOULEVARD", "BOUL", "BOULV", "BLVD"},
new List<string> {"STREET", "ST", "STR"},
// ...
};
var allAbrLookup = new Dictionary<string, List<string>>();
foreach (List<string> list in allAbrList)
{
foreach (string abbr in list)
{
allAbrLookup.Add(abbr, list);
}
}
The last part could be converted into LINQ to have less code, but this way it is easier to understand.
If you don't create a new list for each key, then a Dictionary<string, List<string>> will be fast and reasonably memory-efficient as long as the amount of data isn't enormous. You might also be able to get a little extra benefit from reusing the strings themselves, though the optimizer might take care of that for you anyway.
var abbr = new Dictionary<string, List<string>>;
var values = new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" };
foreach(var aValue in values) abbr.add(value, values);
As Petar Minchev already said, you can split your list into an list of groups and a list of keys that points to this group. To simplify this (in usage) you can write an own implementation of IDictionary and use the Add method to build those groups. I gave it a try and it seems to work. Here are the important parts of the implementation:
public class GroupedDictionary<T> : IDictionary<T,IList<T>>
{
private Dictionary<T, int> _keys;
private Dictionary<int, IList<T>> _valueGroups;
public GroupedDictionary()
{
_keys = new Dictionary<T, int>();
_valueGroups = new Dictionary<int, IList<T>>();
}
public void Add(KeyValuePair<T, IList<T>> item)
{
Add(item.Key, item.Value);
}
public void Add(T key, IList<T> value)
{
// look if some of the values already exist
int existingGroupKey = -1;
foreach (T v in value)
{
if (_keys.Keys.Contains(v))
{
existingGroupKey = _keys[v];
break;
}
}
if (existingGroupKey == -1)
{
// new group
int newGroupKey = _valueGroups.Count;
_valueGroups.Add(newGroupKey, new List<T>(value));
_valueGroups[newGroupKey].Add(key);
foreach (T v in value)
{
_keys.Add(v, newGroupKey);
}
_keys.Add(key, newGroupKey);
}
else
{
// existing group
_valueGroups[existingGroupKey].Add(key);
// add items that are new
foreach (T v in value)
{
if(!_valueGroups[existingGroupKey].Contains(v))
{
_valueGroups[existingGroupKey].Add(v);
}
}
// add new keys
_keys.Add(key, existingGroupKey);
foreach (T v in value)
{
if (!_keys.Keys.Contains(v))
{
_keys.Add(v, existingGroupKey);
}
}
}
}
public IList<T> this[T key]
{
get { return _valueGroups[_keys[key]]; }
set { throw new NotImplementedException(); }
}
}
The usage could look like this:
var groupedDictionary = new GroupedDictionary<string>();
groupedDictionary.Add("BLVD", new List<string> {"BOUL", "BOULV"}); // after that three keys exist and one list of three items
groupedDictionary.Add("BOULEVARD", new List<string> {"BLVD"}); // now there is a fourth key and the key is added to the existing list instance
var items = groupedDictionary["BOULV"]; // will give you the list with four items
Sure it is a lot of work to implement the whole interface but it will give to an encapsulated class that you don't have to worry about, after it is finished.
I don't see a reason to define the value part of your dictionary as a List<string> object, but perhaps that is your requirement. This answer assumes that you just want to know whether the word essentially means "Boulevard".
I would pick one value as the "official" value and map all of the other values to it, like this:
var abbr = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", "BLVD"); // this line may be optional
abbr.Add("BOUL", "BLVD");
abbr.Add("BOULV", "BLVD");
abbr.Add("BOULEVARD", "BLVD");
Alternatively, you could define an enum for the value part of the dictionary, as shown below:
enum AddressLine1Suffix
{
Road,
Street,
Avenue,
Boulevard,
}
var abbr = new Dictionary<string, AddressLine1Suffix>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", AddressLine1Suffix.Boulevard);
abbr.Add("BOUL", AddressLine1Suffix.Boulevard);
abbr.Add("BOULV", AddressLine1Suffix.Boulevard);
abbr.Add("BOULEVARD", AddressLine1Suffix.Boulevard);

Removing duplicates from a list with "priority"

Given a collection of records like this:
string ID1;
string ID2;
string Data1;
string Data2;
// :
string DataN
Initially Data1..N are null, and can pretty much be ignored for this question. ID1 & ID2 both uniquely identify the record. All records will have an ID2; some will also have an ID1. Given an ID2, there is a (time-consuming) method to get it's corresponding ID1. Given an ID1, there is a (time-consuming) method to get Data1..N for the record. Our ultimate goal is to fill in Data1..N for all records as quickly as possible.
Our immediate goal is to (as quickly as possible) eliminate all duplicates in the list, keeping the one with more information.
For example, if Rec1 == {ID1="ABC", ID2="XYZ"}, and Rec2 = {ID1=null, ID2="XYZ"}, then these are duplicates, --- BUT we must specifically remove Rec2 and keep Rec1.
That last requirement eliminates the standard ways of removing Dups (e.g. HashSet), as they consider both sides of the "duplicate" to be interchangeable.
How about you split your original list into 3 - ones with all data, ones with ID1, and ones with just ID2.
Then do:
var unique = allData.Concat(id1Data.Except(allData))
.Concat(id2Data.Except(id1Data).Except(allData));
having defined equality just on the basis of ID2.
I suspect there are more efficient ways of expressing that, but the fundamental idea is sound as far as I can tell. Splitting the initial list into three is simply a matter of using GroupBy (and then calling ToList on each group to avoid repeated queries).
EDIT: Potentially nicer idea: split the data up as before, then do:
var result = new HashSet<...>(allData);
result.UnionWith(id1Data);
result.UnionWith(id2Data);
I believe that UnionWith keeps the existing elements rather than overwriting them with new but equal ones. On the other hand, that's not explicitly specified. It would be nice for it to be well-defined...
(Again, either make your type implement equality based on ID2, or create the hash set using an equality comparer which does so.)
This may smell quite a bit, but I think a LINQ-distinct will still work for you if you ensure the two compared objects come out to be the same. The following comparer would do this:
private class Comp : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
var equalityOfB = x.ID2 == y.ID2;
if (x.ID1 == y.ID1 && equalityOfB)
return true;
if (x.ID1 == null && equalityOfB)
{
x.ID1 = y.ID1;
return true;
}
if (y.ID1 == null && equalityOfB)
{
y.ID1 = x.ID1;
return true;
}
return false;
}
public int GetHashCode(Item obj)
{
return obj.ID2.GetHashCode();
}
}
Then you could use it on a list as such...
var l = new[] {
new Item { ID1 = "a", ID2 = "b" },
new Item { ID1 = null, ID2 = "b" } };
var l2 = l.Distinct(new Comp()).ToArray();
I had a similar issue a couple of months ago.
Try something like this...
public static List<T> RemoveDuplicateSections<T>(List<T> sections) where T:INamedObject
{
Dictionary<string, int> uniqueStore = new Dictionary<string, int>();
List<T> finalList = new List<T>();
int i = 0;
foreach (T currValue in sections)
{
if (!uniqueStore.ContainsKey(currValue.Name))
{
uniqueStore.Add(currValue.Name, 0);
finalList.Add(sections[i]);
}
i++;
}
return finalList;
}
records.GroupBy(r => r, new RecordByIDsEqualityComparer())
.Select(g => g.OrderByDescending(r => r, new RecordByFullnessComparer()).First())
or if you want to merge the records, then Aggregate instead of OrderByDescending/First.

Categories