How to Check All Values in Dictionary is same in C#? - c#

I have a Dictionary, I want to write a method to check whether all values are same in this Dictionary.
Dictionary Type:
Dictionary<string, List<string>>
List {1,2,3}`and {2,1,3} are same in my case.
I have done this previously for simple datatype values, but I can not find logic for new requirement, please help me.
For simple values:
MyDict.GroupBy(x => x.Value).Where(x => x.Count() > 1)
I have also written a Generic Method to compare two datatypes in this way.
// 1
// Require that the counts are equal
if (a.Count != b.Count)
{
return false;
}
// 2
// Initialize new Dictionary of the type
Dictionary<T, int> d = new Dictionary<T, int>();
// 3
// Add each key's frequency from collection A to the Dictionary
foreach (T item in a)
{
int c;
if (d.TryGetValue(item, out c))
{
d[item] = c + 1;
}
else
{
d.Add(item, 1);
}
}
// 4
// Add each key's frequency from collection B to the Dictionary
// Return early if we detect a mismatch
foreach (T item in b)
{
int c;
if (d.TryGetValue(item, out c))
{
if (c == 0)
{
return false;
}
else
{
d[item] = c - 1;
}
}
else
{
// Not in dictionary
return false;
}
}
// 5
// Verify that all frequencies are zero
foreach (int v in d.Values)
{
if (v != 0)
{
return false;
}
}
// 6
// We know the collections are equal
return true;

Implement an IEqualityComparer for List<string> that compares two list based on their content. Then just use Distinct on Values and check the count:
dictionary.Values.Distinct(new ListEqualityComparer()).Count() == 1

This should do the trick
var lists = dic.Select(kv => kv.Value.OrderBy(x => x)).ToList();
var first = lists.First();
var areEqual = lists.Skip(1).All(hs => hs.SequenceEqual(first));
You'll need to add some checks to make this work for the empty case.
...or if you want to take #Selman's approach here's an implementation of the IEqualityComparer:
class SequenceComparer<T>:IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> left, IEnumerable<T> right)
{
return left.OrderBy(x => x).SequenceEqual(right.OrderBy(x => x));
}
public int GetHashCode(IEnumerable<T> item)
{
//no need to sort because XOR is commutative
return item.Aggregate(0, (acc, val) => val.GetHashCode() ^ acc);
}
}
You could make a variant of this combining the best of both approaches using a HashSet<T> that might be considerably more efficient in the case that you have many candidates to test:
HashSet<IEnumerable<int>> hs = new HashSet<IEnumerable<int>>(new SequenceComparer<int>());
hs.Add(dic.First().Value);
var allEqual = dic.All(kvp => !hs.Add(kvp.Value));
This uses the feature of HashSets that disallows adding more than one item that is considered equal with an item already in the set. We make the HashSet use the custom IEqualityComparer above...
So we insert an arbitrary item from the dictionary before we start, then the moment another item is allowed into the set (i.e. hs.Add(kvp.Value) is true), we can say that there's more than one item in the set and bail out early. .All does this automatically.

Selman22's answer works perfectly - you can also do this for your Dictionary<string, List<string>> without having to implement an IEqualityComparer yourself:
var firstValue = dictionary.Values.First().OrderBy(x => x);
return dictionary.Values.All (x => x.OrderBy(y => y).SequenceEqual(firstValue));
We compare the first value to every other value, and check equality in each case. Note that List<string>.OrderBy(x => x) simply sorts the list of strings alphabetically.

Its not the fastest sdolution, but its works for me:
bool AreEqual = l1.Intersect(l2).ToList().Count() == l1.Count() && l1.Count() == l2.Count();

Related

Remove Duplicate value from List<T>

I have one list which has data and sometimes it contains duplicate rows and I want to remove that duplicate row for that I used below code
num = numDetailsTemp.Distinct().ToList();
var query = num.GroupBy(o => new { o.Number })
.Select(group =>
new
{
Name = group.Key,
Numbers = group.OrderByDescending(x => x.Date)
})
.OrderBy(group => group.Numbers.First().Date);
List<NumberDetails> numTemp = new List<NumberDetails>();
foreach (var group in query)
{
foreach (var numb in group.Numbers)
{
numTemp.Add(numb);
break;
}
}
num = numTemp;
The below image shows the duplicate value from the list.
And when I apply remove duplicate it give me an output
But I want to remove that row which not contains alter no or id proof and date like shown in first image first row not, contains AlterNo and ID Proof and date and the second row contains that so I want to remove the first row and display only second row. The date is compulsory to check and after that AlterNo and ID Proof.
You can try the following:
var group =
list
.GroupBy(r => r.Number)
.SelectMany(g => g) //flatten your grouping and filter where you have alterno and id
.Where(r => !string.IsNullOrEmpty(r.AlterNo) && !string.IsNullOrEmpty(r.Id))
.OrderByDescending(r=>r.Date)
.ToList();
You may eliminate duplicates using Distinct operator. First you need to define a comparer class which implements IEqualityComparer interface, and then pass it to the distinct operator in your method.
internal class NumberDetailsComparer : IEqualityComparer<NumberDetails>
{
public bool Equals(NumberDetails x, NumberDetails y)
{
if (\* Set of conditions for equality matching *\)
{
return true;
}
return false;
}
public int GetHashCode(Student obj)
{
return obj.Name.GetHashCode(); // Name or whatever unique property
}
}
And here is how to use it:
var distinctRecords = source.Distinct(new NumberDetailsComparer());
All you need to do is define the criteria for comparer class.
Hope this solves your problem.
This link could be useful for a fully working example:
http://dotnetpattern.com/linq-distinct-operator
So you have a sequence of NumberDetails, and a definition about when you would consider to NumberDetails equal.
Once you have found which NumberDetails are equal, you want to eliminate the duplicates, except one: a duplicate that has values for AlterNo and IdProof.
Alas you didn't specify what you want if there are no duplicates with values for AlterNo and IdProof. Nor what you want if there are several duplicates with values for AlterNo and IdProof.
But let's assume that if there are several of these items, you don't care: just pick one, because they are duplicates anyway.
In your requirement you speak about duplicates. So let's write a class that implements your requirements of equality:
class NumberDetailEqualityComparer : IEqualityComparer<NumberDetail>
{
public static IEQualityComparer<NumberDetail> Default {get;} = new NumberDetaulEqualityComparer();
public bool Equals(NumberDetail x, NumberDetail y)
{
if (x == null) return y == null; // true if both null
if (y == null) return false; // because x not null and y null
if (Object.ReferenceEquals(x, y) return true; // because same object
if (x.GetType() != y.GetType()) return false; // because not same type
// by now we are out of quick checks, we need a value check
return x.Number == y.Number
&& x.FullName == y.FullName
&& ...
// etc, such that this returns true if according your definition
// x and y are equal
}
You also need to implement GetHashCode. You can return anything you want, as long as you
are certain that if x and y are equal, then they return the same HashCode
Furthermore it would be more efficient that if x and y not equal,
then there is a high probability for different HashCode.
Something like:
public int GetHashCode(NumberDetail numberDetail)
{
const int prime1 = 12654365;
const int prime2 = 54655549;
if (numberDetail == null) return prime1;
int hash = prime1;
unsafe
{
hash = prime2 * hash + numberDetail.Number.GetHashCode();
hash = prime2 * hash + numberDetail.FullName.GetHashCode();
hash = prime2 * hash + numberDetail.Date.GetHashCode();
...
}
return hash;
Of course you have to check if any of the properties equal NULL before asking the HashCode.
Obviously in your equality (and thus in GetHashCode) you don't look at AlterNo nor IdProof.
Once that you've defined precisely when you consider two NumberDetails equal, you can make groups of equal NumberDetails
var groupsEqualNumberDetails = numberDetails.GroupBy(
// keySelector: make groups with equal NumberDetails:
numberDetail => numberDetail,
// ResultSelector: take the key and all NumberDetails thas equal this key:
// and keep the first one that has values for AlterNo and IdProof
(key, numberDetailsEqualToKey) => numberDetailsEqualToKey
.Where(numberDetail => numberDetail.AlterNo != null
&& numberDetail.IdProof != null)
.FirstOrDefault(),
// KeyComparer: when do you consider two NumberDetails equal?
NumberDetailEqualityComparer.Default;
}

What to do to get only one List?

Hello i have a method that compares the objects of 2 Lists for differences. Right now this works but only for one property at a time.
Here is the Method:
public SPpowerPlantList compareTwoLists(string sqlServer, string database, DateTime timestampCurrent, string noteCurrent, DateTime timestampOld, string noteOld)
{
int count = 0;
SPpowerPlantList powerPlantListCurrent = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampCurrent, noteCurrent);
SPpowerPlantList powerPlantListOld = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampOld, noteOld);
SPpowerPlantList powerPlantListDifferences = new SPpowerPlantList();
count = powerPlantListOld.Count - powerPlantListCurrent.Count;
var differentObjects = powerPlantListCurrent.Where(p => !powerPlantListOld.Any(l => p.mwWeb == l.mwWeb)).ToList();
foreach (var differentObject in differentObjects)
{
powerPlantListDifferences.Add(differentObject);
}
return powerPlantListDifferences;
}
This works and i get 4 Objects in the new List. The Problem is that i have a few other properties that i need to compare. Instead of mwWeb for example name. When i try to change it i need for every new property a new List and a new Foreach-Loop.
e.g.
int count = 0;
SPpowerPlantList powerPlantListCurrent = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampCurrent, noteCurrent);
SPpowerPlantList powerPlantListOld = loadProjectsAndComponentsFromSqlServer(sqlServer, database, timestampOld, noteOld);
SPpowerPlantList powerPlantListDifferences = new SPpowerPlantList();
SPpowerPlantList powerPlantListDifferences2 = new SPpowerPlantList();
count = powerPlantListOld.Count - powerPlantListCurrent.Count;
var differentObjects = powerPlantListCurrent.Where(p => !powerPlantListOld.Any(l => p.mwWeb == l.mwWeb)).ToList();
var differentObjects2 = powerPlantListCurrent.Where(p => !powerPlantListOld.Any(l => p.shortName == l.shortName)).ToList();
foreach (var differentObject in differentObjects)
{
powerPlantListDifferences.Add(differentObject);
}
foreach (var differentObject in differentObjects2)
{
powerPlantListDifferences2.Add(differentObject);
}
return powerPlantListDifferences;
Is there a way to prevent this? or to make more querys and get only 1 List with all different Objects back?
I tried it with except and intersect but that didnt worked.
So any help or advise would be great and thx for your time.
PS: If there is something wrong with my question-style please say it to me becouse i try to learn to ask better questions.
You may be able to simply chain the properties that you wanted to compare within your Where() clause using OR statements :
// This should get you any elements that have different A properties, B properties, etc.
var different = current.Where(p => !old.Any(l => p.A == l.A || p.B == l.B))
.ToList();
If that doesn't work and you really want to use the Except() or Intersect() methods to properly compare the objects, you could write your own custom IEqualityComparer<YourPowerPlant> to use to properly compare them :
class PowerPlantComparer : IEqualityComparer<YourPowerPlant>
{
// Powerplants are are equal if specific properties are equal.
public bool Equals(YourPowerPlant x, YourPowerPlant y)
{
// Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
// Checks the other properties to compare (examples using mwWeb and shortName)
return x.mwWeb == y.mwWeb && x.shortName == y.shortName;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(YourPowerPlant powerPlant)
{
// Check whether the object is null
if (Object.ReferenceEquals(powerPlant, null)) return 0;
// Get hash code for the mwWeb field if it is not null.
int hashA = powerPlant.mwWeb == null ? 0 : powerPlant.mwWeb.GetHashCode();
// Get hash code for the shortName field if it is not null.
int hashB = powerPlant.shortName == null ? 0 : powerPlant.shortName.GetHashCode();
// Calculate the hash code for the product.
return hashA ^ hashB;
}
}
and then you could likely use something like one of the following depending on your needs :
var different = current.Except(old,new PowerPlantComparer());
or :
var different = current.Intersect(old,new PowerPlantComparer());
One way is to use IEqualityComparer as Rion Williams suggested, if you'd like a more flexible solution you can split logic in to two parts. First create helper method that accepts two lists, and function where you can define what properties you wish to compare. For example :
public static class Helper
{
public static SPpowerPlantList GetDifference(this SPpowerPlantList current, SPpowerPlantList old, Func<PowerPlant, PowerPlant, bool> func)
{
var diff = current.Where(p => old.All(l => func(p, l))).ToList();
var result = new SPpowerPlantList();
foreach (var item in diff) result.Add(item);
return result;
}
}
And use it :
public SPpowerPlantList compareTwoLists(string sqlServer, string database,
DateTime timestampCurrent, string noteCurrent,
DateTime timestampOld, string noteOld)
{
var powerPlantListCurrent = ...;
var powerPlantListOld = ...;
var diff = powerPlantListCurrent.GetDifference(
powerPlantListOld,
(x, y) => x.mwWeb != y.mwWeb ||
x.shortName != y.shortName);
return diff;
}
P.S. if it better suits your needs, you could move method inside of existing class :
public class MyClass
{
public SPpowerPlantList GetDifference(SPpowerPlantList current, SPpowerPlantList old, Func<PowerPlant, PowerPlant, bool> func)
{
...
}
}
And call it (inside of class) :
var result = GetDifference(currentValues, oldValues, (x, y) => x.mwWeb != y.mwWeb);
The easiest way to do this would be to compare some unique identifier (ID)
var differentObjects = powerPlantListCurrent
.Where(p => !powerPlantListOld.Any(l => p.Id == l.Id)
.ToList();
If the other properties might have been updated and you want to check that too, you'll have to compare all of them to detect changes made to existing elements:
Implement a camparison-method (IComparable, IEquatable, IEqualityComparer, or override Equals) or, if that's not possible because you didn't write the class yourself (code generated or external assembly), write a method to compare two of those SPpowerPlantList elements and use that instead of comparing every single property in Linq. For example:
public bool AreThoseTheSame(SPpowerPlantList a,SPpowerPlantList b)
{
if(a.mwWeb != b.mwWeb) return false;
if(a.shortName != b.shortName) return false;
//etc.
return true;
}
Then replace your difference call with this:
var differentObjects = powerPlantListCurrent
.Where(p => !powerPlantListOld.Any(l => AreThoseTheSame(p,l))
.ToList();

Return objects which contain Lists, based on the those lists matching

I have Cell objects which contain a List<int> called PossibleValues. I'm trying to find a way to get a list of cells in which all members have matching PossibleValues. I currently have:
foreach (var cell in group)
{
var cellsWithMatchingPossibleValues = group.Where(c => c.PossibleValues == cell.PossibleValues);
}
Unfortunately this isn't working, I suspect my linq statement isn't comparing the contents of PossibleValues, but instead comparing a reference of some kind, so that even in the case where both lists are composed of 3 and nothing else, cellsWithMatchingPossibleValues ends up only containing one cell, although I'm not certain, or sure how to get around that.
To formalise the question:
How can I return objects which contain Lists based on the those lists matching?
You can implement your own IEqualityComparer<Cell> for your Cell class that states equality when the PossibleValues are equal like this:
public class CellComparer : IEqualityComparer<Cell>
{
public bool Equals(Cell x, Cell y)
{
if (ReferenceEquals(x, null)) return ReferenceEquals(y, null);
if (ReferenceEquals(y, null)) return false;
return x.PossibleValues.SequenceEqual(y.PossibleValues);
}
public int GetHashCode(Cell obj)
{
if (obj == null) return 0;
unchecked
{
int hash = 1;
foreach (int h in obj.PossibleValues.Select(v => v?.GetHashCode() ?? 0))
hash = (hash * 397) ^ h;
return hash;
}
}
}
Then you can use this for a simple LINQ grouping like this:
var cellsGroupedByEqualValues = group.GroupBy(c => c, new CellComparer());
This returns an IEnumerable<IGrouping<Cell,Cell>> and you can iterate through it and receive the number of matching cells:
foreach(var groupedCells in cellsGroupedByEqualValues)
Console.WriteLine(groupedCells.Count());
But these contain duplicates since GroupBy generates a IGrouping for every Cell and adds all matching cells to that. (still trying to find a good way around that)
But for now you can tell for every Cell how many other Cells with the same list of values there are.
You can implement IEqualityComparer interface and use GroupBy method.
Here you can find good GetHashCode for List, and here how to compare a lists.
public class PossibleValuesCellComparer : IEqualityComparer<Cell>
{
public bool Equals(Cell x, Cell y)
{
return Enumerable.SequenceEqual(x.PossibleValues.OrderBy(t => t), y.PossibleValues.OrderBy(t => t));
}
public int GetHashCode(Cell cell)
{
var list = cell.PossibleValues.OrderBy(t => t);
unchecked
{
int hash = 19;
foreach (var obj in list)
{
hash = hash * 31 + obj.GetHashCode();
}
return hash;
}
}
}
....
var g2 = group.GroupBy(x => x, new PossibleValuesCellComparer());

LINQ: Compare two lists and count subset

I am comparing 2 lists and I need to collect occurrences of a subset (modulesToDelete) from the master list (allModules) ONLY when MORE than one occurrence is found. (allModules contains modulesToDelete). Multiple occurrences of any module in modulesToDelete means those modules are being shared. One occurrence of a module in modulesToDelete means that module is isolated and is safe to delete (it just found itself). I can do this with nested foreach loops but this is as far as I got with a LINQ expression (which doesn't work)collect:
List<Module> modulesToDelete = { A, B, C, K }
List<string> allModules = {R, A, B, C, K, D, G, T, B, K } // need to flag B and K
var mods = from mod in modulesToDelete
where allModules.Any(name => name.Contains(mod.Name) && mod.Name.Count() > 1)
select mod;
here is my nested foreach loops which I want to replace with a LINQ expression:
foreach (Module mod in modulesToDelete)
{
int count = 0;
foreach (string modInAllMods in allModules)
{
if (modInAllMods == mod.Name)
{
count++;
}
}
if (count > 1)
{
m_moduleMarkedForKeep.Add(mod);
}
else if( count == 1)
{
// Delete the linked modules
}
}
You can use a lookup which is similar to a dictionary but allows multiple equal keys and returns an IEnumerable<T> as value.
var nameLookup = modulesToDelete.ToLookup(m => m.Name);
var safeToDelete = modulesToDelete.Where(m => nameLookup[m.Name].Count() == 1);
var sharedModules = modulesToDelete.Where(m => nameLookup[m.Name].Count() > 1);
Edit: However, i don't see how allModules is related at all.
Probably easier and with the desired result on your sample data:
var mods = modulesToDelete.Where(m => allModules.Count(s => s == m.Name) > 1);
One way of going about solving this will be to use Intersect function,
Intersection of two string array (ignore case)

How to find duplicate pairs in a Dictionary?

I'd like to calculate the TCC metric:
The Tight Class Cohesion (TCC)
measures the ratio of the number of
method pairs of directly connected
visible methods in a class NDC(C) and
the number of maximal possible method
pairs of connections between the
visible methods of a class NP(C). Two
visible methods are directly
connected, if they are accessing the
same instance variables of the class.
n is the number of visible methods
leading to:
NP(C) = (n(n-1))/2
and
TCC(C) = NDC(C) / NP(C)
So i wrote a method that parse through all methods in the class i want to check. This method stores all methods in that class and there fields they are using in a dictionary that looks like this:
Dictionary<MethodDefinition, IList<FieldReference>> references = new Dictionary<MethodDefinition, IList<FieldReference>>();
So now, how do I iterate through this dictionnary to check the condition mentioned above? If I understand it correctly I have to find these two pairs of methods that are using the same set of fields? Then how can I do this the best way? I think I have to iterate over the dictionary and see if the IList contains the same set? (even not in the same order)?
Any oder ideas`?
My code is the following, but it does not work correctly:
class TCC
{
public static int calculate(TypeDefinition type)
{
int count = 0;
Dictionary<MethodDefinition, HashSet<FieldReference>> references = new Dictionary<MethodDefinition, HashSet<FieldReference>>();
foreach (MethodDefinition method in type.Methods)
{
if (method.IsPublic)
{
references.Add(method, calculateReferences(method));
}
}
for (int i = 0; i < references.Keys.Count; i++)
{
HashSet<FieldReference> list = new HashSet<FieldReference>();
references.TryGetValue(references.Keys.ElementAt(i), out list);
if (isPair(references, list)) {
count++;
}
}
if (count > 0)
{
count = count / 2;
}
return count;
}
private static bool isPair(Dictionary<MethodDefinition, HashSet<FieldReference>> references, HashSet<FieldReference> compare)
{
for (int j = 0; j < references.Keys.Count; j++)
{
HashSet<FieldReference> compareList = new HashSet<FieldReference>();
references.TryGetValue(references.Keys.ElementAt(j), out compareList);
for (int i = 0; i < compare.Count; i++)
{
if (containsAllElements(compareList, compare)) {
return true;
}
}
}
return false;
}
private static bool containsAllElements(HashSet<FieldReference> compareList, HashSet<FieldReference> compare)
{
for (int i = 0; i < compare.Count; i++)
{
if (!compareList.Contains(compare.ElementAt(i)))
{
return false;
}
}
return true;
}
private static HashSet<FieldReference> calculateReferences(MethodDefinition method)
{
HashSet<FieldReference> references = new HashSet<FieldReference>();
foreach (Instruction instruction in method.Body.Instructions)
{
if (instruction.OpCode == OpCodes.Ldfld)
{
FieldReference field = instruction.Operand as FieldReference;
if (field != null)
{
references.Add(field);
}
}
}
return references;
}
}
Well, if you don't mind keeping another dictionary, we can hit this thing with a big-durn-hammer.
Simply put, if we imagine a dictionary where ordered_set(field-references) is the key instead, and we keep a list of the values for each key.... Needless to say this isn't the most clever approach, but it is quick, easy, and uses data structures you are already familiar with.
EG:
hashset< hashset < FieldReference >, Ilist< methods >> Favorite_delicatessen
Build ReferenceSet for method
Look up ReferenceSet in Favorite_Delicatessen
If there:
Add method to method list
Else:
Add Referenceset,method pair
And your methods list is thus the list of methods that share the same state-signature, if you'll let me coin a term.
Since you didn't tell us how can we tell two FieldReferences are duplicated, I will use the default.
LINQ version:
int duplicated = references.SelectMany( p => p.Value )
.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Count();
Can you use ContainsValue to check for duplicates? From what you described it appears you only have duplicates if the values are the same.
How about getting a dictionary where the key is the duplicate item, and the value is a list of keys from the original dictionary that contain the duplicate:
var dupes = references
.SelectMany(k => k.Value)
.GroupBy(v => v)
.Where(g => g.Count() > 1)
.ToDictionary(i => i.Key, i => references
.Where(f => f.Value.Contains(i.Key))
.Select(o => o.Key));

Categories