Is there a collection in C# that guarantees me that I will have only unique elements? I've read about HashSet, but this collection can contain duplicates. Here is my code:
public class Bean
{
public string Name { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
var bean = obj as Bean;
if (bean == null)
{
return false;
}
return this.Name.Equals(bean.Name) && this.Id == bean.Id;
}
public override int GetHashCode()
{
return Name.GetHashCode() * this.Id.GetHashCode();
}
}
You may complain about using non-readonly properties in my GetHashCode method, but this is a way of doing (not the right one).
HashSet<Bean> set = new HashSet<Bean>();
Bean b1 = new Bean {Name = "n", Id = 1};
Bean b2 = new Bean {Name = "n", Id = 2};
set.Add(b1);
set.Add(b2);
b2.Id = 1;
var elements = set.ToList();
var elem1 = elements[0];
var elem2 = elements[1];
if (elem1.Equals(elem2))
{
Console.WriteLine("elements are equal");
}
And in this case, my set contains duplicates.
So is there a collection in C# that guarantees me that it does not contains duplicates?
So is there a collection in C# that guarantees me that it does not
contains duplicates?
There is no existing collection class in C# that does this. You could write your own, but there is no existing one.
Some extra information regarding the issue you are experiencing
If you change a HashSet entry after adding it to the HashSet, then you need to regenerate the HashSet. My below RegenerateHashSet can be used to do that.
The reason you need to regenerate is that duplicate detection only occurs at insertion time (or, in other words, it relies on you not changing an object after you insert it). Which makes sense, if you think about it. The HashSet has no way to detect that an object it contains has changed.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public static class HashSetExtensions
{
public static HashSet<T> RegenerateHashSet<T>(this HashSet<T> original)
{
return new HashSet<T>(original, original.Comparer);
}
}
public class Bean
{
public string Name { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
var bean = obj as Bean;
if (bean == null)
{
return false;
}
return Name.Equals(bean.Name) && Id == bean.Id;
}
public override int GetHashCode()
{
return Name.GetHashCode() * Id.GetHashCode();
}
}
public class Program
{
static void Main(string[] args)
{
HashSet<Bean> set = new HashSet<Bean>();
Bean b1 = new Bean { Name = "n", Id = 1 };
Bean b2 = new Bean { Name = "n", Id = 2 };
set.Add(b1);
set.Add(b2);
b2.Id = 1;
var elements = set.ToList();
var elem1 = elements[0];
var elem2 = elements[1];
if (elem1.Equals(elem2))
{
Console.WriteLine("elements are equal");
}
Console.WriteLine(set.Count);
set = set.RegenerateHashSet();
Console.WriteLine(set.Count);
Console.ReadLine();
}
}
}
Note that the above technique is not bullet-proof - if you add two objects (Object A and Object B) which are duplicates and then change Object B to be different to Object A then the HashSet will still only have one entry in it (since Object B was never added). As such, what you probably want to do is actually store your complete list in a List instead, and then use new HashSet<T>(yourList) whenever you want unique entries. The below class may assist you if you decide to go down that route.
public class RecalculatingHashSet<T>
{
private List<T> originalValues = new List<T>();
public HashSet<T> GetUnique()
{
return new HashSet<T>(originalValues);
}
public void Add(T item)
{
originalValues.Add(item);
}
}
If you don't write your own collection type and handle property changed events to re-evaluate the items, you need to re-evaluate the items at each access. This can be accomplished with LINQ deferred execution:
ICollection<Bean> items= new List<Bean>();
IEnumerable<Bean> reader = items.Distinct();
Rule: only use items to insert or remove elements, use reader for any read access.
Bean b1 = new Bean { Name = "n", Id = 1 };
Bean b2 = new Bean { Name = "n", Id = 2 };
items.Add(b1);
items.Add(b2);
b2.Id = 1;
var elements = reader.ToList();
var elem1 = elements[0];
var elem2 = elements[1]; // throws exception because there is only one element in the result list.
Related
I am having a bit of a frustrating time finding a simple method to compare and prove that the contents of two lists are equal. I have looked at a number of solutions on stackoverflow but I have not been successful. Some of the solutions look like they will require a large amount of work to implement and do something that on the face of it to my mind should be simpler, but perhaps I am too simple to realize that this cannot be done simply :)
I have created a fiddle with some detail that can be viewed here: https://dotnetfiddle.net/cvQr5d
Alternatively please find the full example below, I am having trouble with the object comparison method (variable finalResult) as it's returning false and if the content were being compared I would expect the value to be true:
using System;
using System.Collections.Generic;
using System.Linq;
public class ResponseExample
{
public Guid Id { get; set; } = Guid.Parse("00000000-0000-0000-0000-000000000000");
public int Value { get; set; } = 0;
public string Initials { get; set; } = "J";
public string FirstName { get; set; } = "Joe";
public string Surname { get; set; } = "Blogs";
public string CellPhone { get; set; } = "0923232199";
public bool EmailVerified { get; set; } = false;
public bool CellPhoneVerified { get; set; } = true;
}
public class Program
{
public static void Main()
{
var responseOne = new ResponseExample();
var responseTwo = new ResponseExample();
var responseThree = new ResponseExample();
var responseFour = new ResponseExample();
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseThree);
objectListTwo.Add(responseFour);
bool result = objectListOne.Count == objectListTwo.Count();
Console.WriteLine($"Count: {result}");
bool finalResult = ScrambledEquals<ResponseExample>(objectListOne, objectListTwo);
Console.WriteLine($"Object compare: {finalResult}");
}
//https://stackoverflow.com/a/3670089/3324415
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T,
int>();
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
}
As people in comments have pointed out this will not work as comparing a complex type by default compares whether the reference is the same. Field by field comparison will not work without implementing equality methods (and then you would need to overload GetHashCode and so on). See https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0
However, if you can use c# 9, which is what you have in the fiddle you can define the type as a record instead of class. Records have built in field by field comparison. See https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#characteristics-of-records
So public class ResponseExample would become public record ResponseExample and your code works as you expect.
Use Enumerable.All<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) Method which Determines whether all elements of a sequence satisfy a condition.
Once you have initilized your two List
list1.All(x=>list2.Contains(x))
This works by ensuring that all elements in list2 are containted in list1 otherwise returns false
Your method as is will compare if the 2 lists contain the same objects. So it is returning false as there are 4 different objects. If you create your list like this, using the same objects, it will return true:
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseTwo);
objectListTwo.Add(responseOne);
To get a true value when the contents of the objects are the same you could serialize the objects into a json string like this:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
JavaScriptSerializer json = new JavaScriptSerializer();
var cnt = new Dictionary<string,
int>();
foreach (T _s in list1)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T _s in list2)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
If the performance is not a big deal, you can use Newtonsoft.Json. We will be able to compare different types of objects as well as run a deep equals check.
First install the package:
Install-Package Newtonsoft.Json
Here is the code snip:
public static bool DeepEqualsUsingJson<T>(IList<T> l1, IList<T> l2)
{
if (ReferenceEquals(l1, l2))
return true;
if (ReferenceEquals(l2, null))
return false;
if (l1.Count != l2.Count)
return false;
var l1JObject = l1.Select(i => JObject.FromObject(i)).ToList();
var l2JObject = l2.Select(i => JObject.FromObject(i)).ToList();
foreach (var o1 in l1JObject)
{
var index = l2JObject.FindIndex(o2 => JToken.DeepEquals(o1, o2));
if (index == -1)
return false;
l2JObject.RemoveAt(index);
}
return l2JObject.Count == 0;
}
If you have made a list of Custom objects is it a must to have to do with Hashcodes if you wanna check that list to see if it contains a object before adding it, I mean so that you do not get duplicates in the list or is there an easier way basically I want to use the contains method on a custom object list to see if the object I want to add already exists in the list and if there then is an easier way then to have to deal with hashcodes?
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataConverter.Objects;
namespace DataConverter.Converters
{
class CategoryConverter
{
private Category category;
private SubCategory subCategory;
private ExcellObj excellObj;
public CategoryConverter(string path)
{
excellObj = new ExcellObj(path);
}
public List<Category> getCategoryListExcel()
{
List<Category> categories = new List<Category>();
List<string> ColumnNames = new List<string> { "Group1", "Group1Descr" };
List<int> CorrectColumn = new List<int>();
for(int i = 0; i < ColumnNames.Count; i++)
{
CorrectColumn.Add(excellObj.findColumn(ColumnNames[i]));
}
for(int i = 2; i < excellObj.allRows; i++)
{
categories.Add(category = new Category(excellObj.getValuesFromCell(i, CorrectColumn[1]), excellObj.getValuesFromCell(i, CorrectColumn[0]), "Home"));
}
return categories;
}
public List<List<SubCategory>> getSubCategory()
{
List<SubCategory> subCategories1 = new List<SubCategory>();
List<SubCategory> subCategories2 = new List<SubCategory>();
List<List<SubCategory>> subCategoriesList = new List<List<SubCategory>>();
List<string> ColumnNamesSubCategory1 = new List<string> { "Group2", "Group2Descr" };
List<string> ColumnNamesSubCategory2 = new List<string> { "Group3", "Group3Desc" };
List<int> CorrectColumn1 = new List<int>();
List<int> CorrectColumn2 = new List<int>();
for(int i = 0; i < ColumnNamesSubCategory1.Count; i++)
{
CorrectColumn1.Add(excellObj.findColumn(ColumnNamesSubCategory1[i]));
CorrectColumn2.Add(excellObj.findColumn(ColumnNamesSubCategory2[i]));
}
for(int i = 1; i < excellObj.allRows; i++)
{
subCategories1.Add(subCategory = new SubCategory(excellObj.getValuesFromCell(i, CorrectColumn1[1]),excellObj.getValuesFromCell(i,CorrectColumn1[0]), "Home"));
subCategories2.Add(subCategory = new SubCategory(excellObj.getValuesFromCell(i,CorrectColumn2[1]), excellObj.getValuesFromCell(i,CorrectColumn2[0]), "Home"));
}
subCategoriesList.Add(subCategories1);
subCategoriesList.Add(subCategories2);
return subCategoriesList;
}
public void finnishedUsingExcel()
{
excellObj.CloseApplication();
}
}
}
and what i whant to happen is that i whant to run a
if(categories.Contains(category) == false){
categories.add(category)
}
i do not understand this part in the documentation?
public Person(string lastName, string ssn)
{
if (Regex.IsMatch(ssn, #"\d{9}"))
uniqueSsn = $"{ssn.Substring(0, 3)}-{ssn.Substring(3, 2)}-{ssn.Substring(5, 4)}";
else if (Regex.IsMatch(ssn, #"\d{3}-\d{2}-\d{4}"))
uniqueSsn = ssn;
else
throw new FormatException("The social security number has an invalid format.");
this.LastName = lastName;
}
Assuming you have a code like this:
List<CustomObject> listOfCustomObjects = new List<CustomObject>();
Solution 1
If so, you can use listOfCustomObjects.Contains(customObject) to find out if customObject is in listOfCustomObjects. You should add using System.Linq; to the top of your code in order to use this method.
Solution 2
Another way to not have duplicates in your list is basically not using a List. You can use HashSet instead. With this method, duplicate objects won't be added to your list automatically. HashSet is also in LINQ Library, so you should add the line using System.Linq; for this solution too. Here's an example how to create a new HashSet with your CustomObject class:
HashSet<CustomObject> setOfCustomObjects = new HashSet<CustomObject>();
You really should have your class implement IEquatable if it's reasonable to do so and you're going to check for equality with any frequency, just so it does not bite you. The "Contains" method will work, but only to test that the exact same instance is present, not necessarily one that just shares matching properties. Consider the following code:
class Program
{
static void Main(string[] args)
{
var classInstance = new MySampleClass("testA", "testB");
var classList = new List<MySampleClass>();
classList.Add(classInstance);
if (classList.Contains(new MySampleClass("testA", "testB")))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
if (classList.Contains(classInstance))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
}
public class MySampleClass
{
public string SampleProperty1 { get; set; }
public string SampleProperty2 { get; set; }
public MySampleClass(string sampleProperty1, string sampleProperty2)
{
SampleProperty1 = sampleProperty1;
SampleProperty2 = sampleProperty2;
}
}
Even though we're checking for the presence of the class that has the exact same values as the one we previously added, they're still separate instances and you'll end up with duplicates in your list.
An alternative in the very limited case would be to use a LINQ method to check whether the list already contains an entry with a property that can be compared, such as an int ID or something:
yourList.Any(item => item.Id.Equals(otherItem.Id));
Again, if it's more than a one off, implement it the right way with IEquatable. See Microsoft's documentation
Consider objects of the following type:
public class MyObject
{
// "defining" attributes
private string member1;
private string member2;
private string member3;
// other attributes
private string member4;
private string member5;
// ctor
public MyObject(){}
public bool compare(MyObject that)
{
// compare this object with another (that)
}
The compare() method should behave as follows. It only considers "defining" attributes. If they are all different between two objects, it should return false. If they are all the same, return false. In other cases, return true (if only one or two of them differ between the two objects).
The question is, do I have to resort to a huge if statement for this? Is there a "better" solution?
Instead of creating n number of strings, you can create property called List<string> DefiningAttributes and List<string> OtherAttributes.
Now add values to this lists where you want, for now I am doing it in constructor. Use Except() method to get difference from DefiningAttributes and OtherAttributes
Check below implementation
public class MyObject
{
// "defining" attributes
public List<string> DefiningAttributes { get; set; }
// other attributes
public List<string> OtherAttributes { get; set; }
public MyObject()
{
//I used constructor to assign values
DefiningAttributes = new List<string>() { "ABC", "PQR", "XYZ" };
OtherAttributes = new List<string>() { "ABC", "PQR", "Stackoverflow" };
}
public bool compare(MyObject that)
{
var difference = this.DefiningAttributes.Except(that.DefiningAttributes);
//Return false If they are all different between two objects OR if they are all same
if(difference.Count() == this.DefiningAttributes.Count() || !difference.Any())
return false;
//Otherwise return true
return true;
}
}
For more details, read Enumerable.Except method
I think this should do it
var comp1 = this.member1 == that.member1;
var comp2 = this.member2 == that.member2;
var comp3 = this.member3 == that.member3;
var comparisons = new List<string>() { comp2, comp3 };
return comparisons.Any(val => val != comp1 );
comp1, comp2 and comp3 will be bools. If any of those comparisons are not the same as the first comparison*, we know we have different results.
[*] You could use any reference point instead of the first comparison
Edit: Whoops, I thought this was a javascript question, but I then realized it was C#. I just changed my answer to use C# syntax, but the idea is the same. This requires the Linq extension method Any.
The following code should do the trick.
If you want to increase the number of defining properties you just edit the size of the array or swap it to a list.
It should iterate over them and when one does not mach return true.
If at the end none matches returns false.
public class MyObject
{
// "defining" attributes
public string[] definingAttributes = new string[3];
// other attributes
private string member4;
private string member5;
// ctor
public MyObject() { }
public bool compare(MyObject that)
{
bool? previousResult = null;
// compare this object with another (that)
for (int i = 0; i < definingAttributes.Length; i++)
{
if (previousResult == null)
{
previousResult = definingAttributes[i] == that.definingAttributes[i];
}
if (definingAttributes[i] != that.definingAttributes[i])
{
if (previousResult != (definingAttributes[i] == that.definingAttributes[i]))
{
return true;
}
}
}
return false;
}
}
Question moved here.
My requirement is to write a program that sort of mimics diff tools. Yes there are quite a few libraries and open source code that accomplishes this purpose, but I would like to write my own comparer.
Here's the starting point. I have a class called DataItem which looks like this:
public class DataItem
{
public DataItem() { }
public DataItem(string d, string v) { Data = d; Value = v; }
public string Data { get; set; }
public string Value { get; set; }
}
I have two lists of these class objects, let's call them PRE and POST and take some example values as follows. 'Data' part will be unique in a list.
preList: (Data,Value)
AAA,0
BBB,1
CCC,3
DDD,4
FFF,0
GGG,3
postList: (Data,Value)
AAA,0
BBB,2
DDD,4
EEE,9
FFF,3
Think of PRE as the original list, and POST as the list after some changes done. I would like to compare the two, and categorize them into three categories:
Added Items - An item with a new 'Data' added to the list.
Removed Items - An item was removed from the list.
Diff Items - 'Data' is found in both PRE and POST lists, but their corresponding 'Value' is different.
So when categorized they should look like this:
Added Items:
EEE,9
Removed Items:
CCC,3
GGG,3
Diff Items:
BBB
FFF
I have another DiffItem class, to objects of which I would like to put the final results. DiffItem looks like this:
public class DiffItem
{
public DiffItem() { }
public DiffItem(string data, string type, string pre, string post) { Data = data; DiffType = type; PreVal = pre; PostVal = post; }
public string Data { get; set; }
public string DiffType { get; set; } // DiffType = Add/Remove/Diff
public string PreVal { get; set; } // preList value corresponding to Data item
public string PostVal { get; set; } // postList value corresponding to Data item
}
To accomplish this, first I extended IEqualityComparer and wrote a couple of comparers:
public class DataItemComparer : IEqualityComparer<DataItem>
{
public bool Equals(DataItem x, DataItem y)
{
return (string.Equals(x.Data, y.Data) && string.Equals(x.Value, y.Value));
}
public int GetHashCode(DataItem obj)
{
return obj.Data.GetHashCode();
}
}
public class DataItemDataComparer : IEqualityComparer<DataItem>
{
public bool Equals(DataItem x, DataItem y)
{
return string.Equals(x.Data, y.Data);
}
public int GetHashCode(DataItem obj)
{
return obj.Data.GetHashCode();
}
}
Then used Except() and Intersect() methods as follows:
static void DoDiff()
{
diffList = new List<DiffItem>();
IEnumerable<DataItem> preOnly = preList.Except(postList, new DataItemComparer());
IEnumerable<DataItem> postOnly = postList.Except(preList, new DataItemComparer());
IEnumerable<DataItem> common = postList.Intersect(preList, new DataItemComparer());
IEnumerable<DataItem> added = postOnly.Except(preOnly, new DataItemDataComparer());
IEnumerable<DataItem> removed = preOnly.Except(postOnly, new DataItemDataComparer());
IEnumerable<DataItem> diffPre = preOnly.Intersect(postOnly, new DataItemDataComparer());
IEnumerable<DataItem> diffPost = postOnly.Intersect(preOnly, new DataItemDataComparer());
foreach (DataItem add in added)
{
diffList.Add(new DiffItem(add.Data, "Add", null, add.Value));
}
foreach (DataItem rem in removed)
{
diffList.Add(new DiffItem(rem.Data, "Remove", rem.Value, null));
}
foreach (DataItem pre in diffPre)
{
DataItem post = diffPost.First(x => x.Data == pre.Data);
diffList.Add(new DiffItem(pre.Data, "Diff", pre.Value, post.Value));
}
}
This does work and gets the job done. But I'm wondering if there's a 'better' way to do this. Note that I put quotes around the word 'better', because I don't have a proper definition for what would make this better. Perhaps is there a way to get this done without as many 'foreach' loops and use of Except() and Intersetc(), since I would imagine that behind the Linq there's quite a bit of iterations going on.
Simply put, is there a cleaner code that I can write for this? I'm asking mostly out of academic interest and to expand my knowledge.
I don't think you need your IEqualityComparer:
var added = from a in postList
where !preList.Any(b => b.Data == a.Data)
select new DiffItem(a.Data, "Add", null, a.Value);
var removed = from b in preList
where !postList.Any(a => a.Data == b.Data)
select new DiffItem(b.Data, "Remove", b.Value, null);
var diff = from b in preList
join a in postList on b.Data equals a.Data
where b.Value != a.Value
select new DiffItem(b.Data, "Diff", b.Data, a.Data);
var diffList = added.ToList();
diffList.AddRange(removed);
diffList.AddRange(diff);
I am trying to figure out the best way to organise a bunch of my data classes, given I need to be able to access some metrics on them all at some point.
Here's a snippet of my OR class:
public enum status { CLOSED, OPEN }
public class OR
{
public string reference { get; set; }
public string title { get; set; }
public status status { get; set; }
}
Not every OR I initialise will have values for all properties. I want to be able to 'collect' thousands of these together in such a way that I can easily obtain a count of how many OR objects had a value set. For example:
OR a = new OR() { reference = "a" }
OR b = new OR() { reference = "b", title = "test" }
OR c = new OR() { reference = "c", title = "test", status = status.CLOSED }
Now these are somehow collected in such a way I can do (pseudo):
int titleCount = ORCollection.titleCount;
titleCount = 2
I would also want to be able gather metrics for the enum type properties, for example retrieve a Dictionary from the collection that looks like:
Dictionary<string, int> statusCounts = { "CLOSED", 1 }
The reason for wanting access to these metrics is that I am building two collections of ORs and comparing them side-by-side for any differences (they should be identical). I want to be able to compare their metrics at this higher level first, then break-down where precisely they differ.
Thanks for any light that can be shed on how to accomplish this. :-)
... to 'collect' thousands of these
Thousands is not a huge number. Just use a List<OR> and you can get all your metrics with Linq queries.
For example:
List<OR> orList = ...;
int titleCount = orList
.Where(o => ! string.IsNullOrEmpty(o.title))
.Count();
Dictionary<status, int> statusCounts = orList
.GroupBy(o => o.status)
.ToDictionary(g => g.Key, g => g.Count());
The existing answers using Linq are absolutely great and really elegant, so the idea presented below is just for posterity.
Here is a (very rough) reflection-based program that will alow you to count the "valid" properties in any collection of objects.
The validators are defined by you in the Validators dictionary so that you can easily change what is a valid/invalid value for each property. You may find it useful as a concept if you end up with objects having tons of properties and don't want to have to write inline linq metrics on the actual collection itself for every single property.
You could weaponise this as a function and then run it against both collections, giving you a basis to report on the exact differences between both since it records the references to the individual objects in the final dictionary.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace reftest1
{
public enum status { CLOSED, OPEN }
public class OR
{
public string reference { get; set; }
public string title { get; set; }
public status status { get; set; }
public int foo { get; set; }
}
//creates a dictionary by property of objects whereby that property is a valid value
class Program
{
//create dictionary containing what constitues an invalid value here
static Dictionary<string,Func<object,bool>> Validators = new Dictionary<string, Func<object,bool>>
{
{"reference",
(r)=> { if (r ==null) return false;
return !String.IsNullOrEmpty(r.ToString());}
},
{"title",
(t)=> { if (t ==null) return false;
return !String.IsNullOrEmpty(t.ToString());}
},
{"status", (s) =>
{
if (s == null) return false;
return !String.IsNullOrEmpty(s.ToString());
}},
{"foo",
(f) =>{if (f == null) return false;
return !(Convert.ToInt32(f.ToString()) == 0);}
}
};
static void Main(string[] args)
{
var collection = new List<OR>();
collection.Add(new OR() {reference = "a",foo=1,});
collection.Add(new OR(){reference = "b", title = "test"});
collection.Add(new OR(){reference = "c", title = "test", status = status.CLOSED});
Type T = typeof (OR);
var PropertyMetrics = new Dictionary<string, List<OR>>();
foreach (var pi in GetProperties(T))
{
PropertyMetrics.Add(pi.Name,new List<OR>());
foreach (var item in collection)
{
//execute validator if defined
if (Validators.ContainsKey(pi.Name))
{
//get actual property value and compare to valid value
var value = pi.GetValue(item, null);
//if the value is valid, record the object into the dictionary
if (Validators[pi.Name](value))
{
var lookup = PropertyMetrics[pi.Name];
lookup.Add(item);
}
}//end trygetvalue
}
}//end foreach pi
foreach (var metric in PropertyMetrics)
{
Console.WriteLine("Property '{0}' is set in {1} objects in collection",metric.Key,metric.Value.Count);
}
Console.ReadLine();
}
private static List<PropertyInfo> GetProperties(Type T)
{
return T.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
}
}
}
You can get the title count using this linq query:
int titleCount = ORCollection
.Where(x => !string.IsNullOrWhiteSpace(x.title))
.Count();
You could get the count of closed like this:
int closedCount = ORCollection
.Where(x => x.status == status.CLOSED)
.Count();
If you were going to have larger collections or you access the values a lot it might be worth creating a custom collection implementation that stores the field counts, it could then increment/decrement these values as you add and remove items. You could also store a dictionary of status counts in this custom collection that gets updated as you add and remove items.