Related
I have a Class named Privilegeswith the following properties int UserId,string FormName,string CompName,int Privilege
And I have 2 lists of Privileges type with different values as the sample below
List<Privileges> list1 = new List<Privileges>(){
new Privileges(){UserId= 1,FormName="Form1",CompName="Button1",Privilege=2},
new Privileges(){UserId= 2,FormName="Form1",CompName="Button3",Privilege=3},
new Privileges(){UserId= 3,FormName="Form2",CompName="Button2",Privilege=2}
};
List<Privileges> list2 = new List<Privileges>(){
new Privileges(){UserId= 5,FormName="Form1",CompName="Button1",Privilege=2},
new Privileges(){UserId= 2,FormName="Form1",CompName="Button3",Privilege=4},
new Privileges(){UserId= 4,FormName="Form2",CompName="Button2",Privilege=3}
};
I want to make 3 functions
I made the first one which returns matched elements between the 2 lists
and the result is the following
{UserId= 2,FormName="Form1",CompName="Button3",Privilege=3}
The 2nd function should return elements that exist in the first list and not in the second list, with the following result
{UserId= 1,FormName="Form1",CompName="Button1",Privilege=2},
{UserId= 3,FormName="Form2",CompName="Button2",Privilege=2}
The 3rd function should return elements that exist in the second list and not in the first list, with the following result
{UserId= 5,FormName="Form1",CompName="Button1",Privilege=2},
{UserId= 4,FormName="Form2",CompName="Button2",Privilege=3}
The matching clause should compare UserId,FormName,CompName values regardless what the value of privilege is.
you can check my code snippet here
You don't have to write any complex LINQ statements for these (and many more) tasks. Just define an IEqualityComparer and everything becomes almost ridiculously simple:
class PrivilegesComparer : IEqualityComparer<Privileges>
{
public bool Equals(Privileges x, Privileges y)
{
return x.UserId == y.UserId
&& x.FormName == y.FormName
&& x.CompName == y.CompName;
}
public int GetHashCode(Privileges obj)
{
return (obj.UserId + obj.FormName + obj.CompName).GetHashCode();
}
}
Usage:
var comparer = new PrivilegesComparer();
var intersect = list1.Intersect(list2, comparer);
var l1Exceptl2 = list1.Except(list2, comparer);
var l2Exceptl1 = list2.Except(list1, comparer);
Which represent your first, second and third function, respectively.
That's quite different from writing a complex LINQ statement for each individual task.
Elements in list1 not in list2
var itemsInList1NotInList2 = list1.Where(l1 => !list2.Any(l2 => l1.UserId == l2.UserId && l1.FormName == l2.FormName && l1.CompName == l2.CompName)).ToList();
Elements in list2 not in list1
var itemsInList2NotInList1 = list2.Where(l2 => !list1.Any(l1 => l1.UserId == l2.UserId && l1.FormName == l2.FormName && l1.CompName == l2.CompName)).ToList();
I'm looping through the items in my database using C# .NET and I'm attempting to display different data dependant on if a column value matches any of the values in an array. Because my array could potentially have hundreds of values, I'd rather not create hundreds of different IF statements, if possible. Is there a simpler way to achieve this?
Here's some example code, where "Items" is my db data and "Categories" is a column of said data:
var categoryList = new List<int> { 1, 2, 3, 4 };
foreach(var item in Items){
if(item.Categories.Any(x => #categoryList.Equals(x))){
<p>Yes</p>
}else{
<p>No</p>
}
}
The answer I give is based on the answer of this question. I modified the code to your situation.
foreach(var item in Items)
{
bool hasCategory = categoryList.Any(x => item.Categories.Any(c => c.Id == x));
}
or for larger collections (performance-wise):
bool hasCategory = item.Categories.Select(c => c.Id)
.Intersect(categoryList)
.Any();
Edit:
At first I thought item.Categories was a collection of IDs or something but then I started doubting. If item.Categories is just a single integer, following code will work:
foreach(var item in Items)
{
if(categoryList.Any(x => x == item.Categories))
<p>Yes</p>
else
<p>No</p>
}
This question already has answers here:
Comparing two collections for equality irrespective of the order of items in them
(21 answers)
Closed 5 years ago.
Yet another list-comparing question.
List<MyType> list1;
List<MyType> list2;
I need to check that they both have the same elements, regardless of their position within the list. Each MyType object may appear multiple times on a list. Is there a built-in function that checks this? What if I guarantee that each element appears only once in a list?
EDIT: Guys thanks for the answers but I forgot to add something, the number of occurrences of each element should be the same on both lists.
If you want them to be really equal (i.e. the same items and the same number of each item), I think that the simplest solution is to sort before comparing:
Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))
Edit:
Here is a solution that performs a bit better (about ten times faster), and only requires IEquatable, not IComparable:
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);
}
Edit 2:
To handle any data type as key (for example nullable types as Frank Tzanabetis pointed out), you can make a version that takes a comparer for the dictionary:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
var cnt = new Dictionary<T, int>(comparer);
...
If you don't care about the number of occurrences, I would approach it like this. Using hash sets will give you better performance than simple iteration.
var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);
This will require that you have overridden .GetHashCode() and implemented IEquatable<MyType> on MyType.
As written, this question is ambigous. The statement:
... they both have the same elements, regardless of their position within the list.
Each MyType object may appear multiple times on a list.
does not indicate whether you want to ensure that the two lists have the same set of objects or the same distinct set.
If you want to ensure to collections have exactly the same set of members regardless of order, you can use:
// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();
If you want to ensure two collections have the same distinct set of members (where duplicates in either are ignored), you can use:
// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();
Using the set operations (Intersect, Union, Except) is more efficient than using methods like Contains. In my opinion, it also better expresses the expectations of your query.
EDIT: Now that you've clarified your question, I can say that you want to use the first form - since duplicates matter. Here's a simple example to demonstrate that you get the result you want:
var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };
// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any();
In addition to Guffa's answer, you could use this variant to have a more shorthanded notation.
public static bool ScrambledEquals<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
{
var deletedItems = list1.Except(list2).Any();
var newItems = list2.Except(list1).Any();
return !newItems && !deletedItems;
}
Thinking this should do what you want:
list1.All(item => list2.Contains(item)) &&
list2.All(item => list1.Contains(item));
if you want it to be distinct, you could change it to:
list1.All(item => list2.Contains(item)) &&
list1.Distinct().Count() == list1.Count &&
list1.Count == list2.Count
This is a slightly difficult problem, which I think reduces to: "Test if two lists are permutations of each other."
I believe the solutions provided by others only indicate whether the 2 lists contain the same unique elements. This is a necessary but insufficient test, for example
{1, 1, 2, 3} is not a permutation of {3, 3, 1, 2}
although their counts are equal and they contain the same distinct elements.
I believe this should work though, although it's not the most efficient:
static bool ArePermutations<T>(IList<T> list1, IList<T> list2)
{
if(list1.Count != list2.Count)
return false;
var l1 = list1.ToLookup(t => t);
var l2 = list2.ToLookup(t => t);
return l1.Count == l2.Count
&& l1.All(group => l2.Contains(group.Key) && l2[group.Key].Count() == group.Count());
}
This worked for me:
If you are comparing two lists of objects depend upon single entity like ID, and you want a third list which matches that condition, then you can do the following:
var list3 = List1.Where(n => !List2.select(n1 => n1.Id).Contains(n.Id));
Refer: MSDN - C# Compare Two lists of objects
I use this method )
public delegate bool CompareValue<in T1, in T2>(T1 val1, T2 val2);
public static bool CompareTwoArrays<T1, T2>(this IEnumerable<T1> array1, IEnumerable<T2> array2, CompareValue<T1, T2> compareValue)
{
return array1.Select(item1 => array2.Any(item2 => compareValue(item1, item2))).All(search => search)
&& array2.Select(item2 => array1.Any(item1 => compareValue(item1, item2))).All(search => search);
}
try this!!!
using following code you could compare one or many fields to generate a result list as per your need. result list will contain only modified item(s).
// veriables been used
List<T> diffList = new List<T>();
List<T> gotResultList = new List<T>();
// compare First field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
// compare Second field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
MessageBox.Show(diffList.Count.ToString);
This question already has answers here:
Comparing two collections for equality irrespective of the order of items in them
(21 answers)
Closed 5 years ago.
Yet another list-comparing question.
List<MyType> list1;
List<MyType> list2;
I need to check that they both have the same elements, regardless of their position within the list. Each MyType object may appear multiple times on a list. Is there a built-in function that checks this? What if I guarantee that each element appears only once in a list?
EDIT: Guys thanks for the answers but I forgot to add something, the number of occurrences of each element should be the same on both lists.
If you want them to be really equal (i.e. the same items and the same number of each item), I think that the simplest solution is to sort before comparing:
Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))
Edit:
Here is a solution that performs a bit better (about ten times faster), and only requires IEquatable, not IComparable:
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);
}
Edit 2:
To handle any data type as key (for example nullable types as Frank Tzanabetis pointed out), you can make a version that takes a comparer for the dictionary:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
var cnt = new Dictionary<T, int>(comparer);
...
If you don't care about the number of occurrences, I would approach it like this. Using hash sets will give you better performance than simple iteration.
var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);
This will require that you have overridden .GetHashCode() and implemented IEquatable<MyType> on MyType.
As written, this question is ambigous. The statement:
... they both have the same elements, regardless of their position within the list.
Each MyType object may appear multiple times on a list.
does not indicate whether you want to ensure that the two lists have the same set of objects or the same distinct set.
If you want to ensure to collections have exactly the same set of members regardless of order, you can use:
// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();
If you want to ensure two collections have the same distinct set of members (where duplicates in either are ignored), you can use:
// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();
Using the set operations (Intersect, Union, Except) is more efficient than using methods like Contains. In my opinion, it also better expresses the expectations of your query.
EDIT: Now that you've clarified your question, I can say that you want to use the first form - since duplicates matter. Here's a simple example to demonstrate that you get the result you want:
var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };
// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any();
In addition to Guffa's answer, you could use this variant to have a more shorthanded notation.
public static bool ScrambledEquals<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
{
var deletedItems = list1.Except(list2).Any();
var newItems = list2.Except(list1).Any();
return !newItems && !deletedItems;
}
Thinking this should do what you want:
list1.All(item => list2.Contains(item)) &&
list2.All(item => list1.Contains(item));
if you want it to be distinct, you could change it to:
list1.All(item => list2.Contains(item)) &&
list1.Distinct().Count() == list1.Count &&
list1.Count == list2.Count
This is a slightly difficult problem, which I think reduces to: "Test if two lists are permutations of each other."
I believe the solutions provided by others only indicate whether the 2 lists contain the same unique elements. This is a necessary but insufficient test, for example
{1, 1, 2, 3} is not a permutation of {3, 3, 1, 2}
although their counts are equal and they contain the same distinct elements.
I believe this should work though, although it's not the most efficient:
static bool ArePermutations<T>(IList<T> list1, IList<T> list2)
{
if(list1.Count != list2.Count)
return false;
var l1 = list1.ToLookup(t => t);
var l2 = list2.ToLookup(t => t);
return l1.Count == l2.Count
&& l1.All(group => l2.Contains(group.Key) && l2[group.Key].Count() == group.Count());
}
This worked for me:
If you are comparing two lists of objects depend upon single entity like ID, and you want a third list which matches that condition, then you can do the following:
var list3 = List1.Where(n => !List2.select(n1 => n1.Id).Contains(n.Id));
Refer: MSDN - C# Compare Two lists of objects
I use this method )
public delegate bool CompareValue<in T1, in T2>(T1 val1, T2 val2);
public static bool CompareTwoArrays<T1, T2>(this IEnumerable<T1> array1, IEnumerable<T2> array2, CompareValue<T1, T2> compareValue)
{
return array1.Select(item1 => array2.Any(item2 => compareValue(item1, item2))).All(search => search)
&& array2.Select(item2 => array1.Any(item1 => compareValue(item1, item2))).All(search => search);
}
try this!!!
using following code you could compare one or many fields to generate a result list as per your need. result list will contain only modified item(s).
// veriables been used
List<T> diffList = new List<T>();
List<T> gotResultList = new List<T>();
// compare First field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
// compare Second field within my MyList
gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2))).ToList();
// Generate result list
diffList.AddRange(gotResultList);
MessageBox.Show(diffList.Count.ToString);
This is what i have now as a very basic search:
var Results = from p in dx.Listings select p;
if (CategoryId > 0) Results = Results.Where(p => p.CategoryId == CategoryId);
if (SuburbId > 0) Results = Results.Where(p => p.SuburbId == SuburbId);
var OrderedResults = Results.OrderByDescending(p => p.ListingType);
OrderedResults = OrderedResults.ThenByDescending(p => p.Created);
I understand that i can add in a .Contains() or similar and put in keywords from a keyword box (split into individual items) and that should get the list of results.
However i need to order the results by basic relevance. Meaning that if record A contains 2 of the keywords (in the 'Body' nvarchar(MAX) field) it should be higher than record B that only matches against 1 of the keywords. I don't need a full count of every hit... however if thats eaiser to manage that would be fine.
So is there any way to get the hit count directly in as part of the orderby nicely? I can manage it by getting the results and parsing however i really don't want to do that as parsing possibly thousands could chug the IIS machine, while the SQL Server is a decently powerful cluster :)
If anyone has any ideas or tips it would be a big help.
If I understand you correctly you want to call OrderyByDescending( p => p.Body ) but it should be ordered by how many times a certain word appreas in p.Body ?
Then you should be able to create a method that counts the occurrences and returns the count number then you can simply do OrderyByDescending( p => CountOccurences(p.Body) )
You can alternatively create a BodyComparer class that implements IComparer and then pass it to OrderByDescending
EDIT:
take a look a this link Enable Full Text Searching
Here is a simple example, if I understand what you're looking for correctly:
var storedData = new[]{
new int[] {1, 2, 3, 4},
new int[] {1, 2, 3, 4, 5}
};
var itemsFromTextBox = new[] { 3, 4, 5 };
var query = storedData.Where(a => a.ContainsAny(itemsFromTextBox))
.OrderByDescending(a => itemsFromTextBox.Sum(i => a.Contains(i)? 1:0));
With the following ContainsAny extension:
public static bool ContainsAny<T>(this IEnumerable<T> e1, IEnumerable<T> e2)
{
foreach (var item in e2)
{
if (e1.Contains(item)) return true;
}
return false;
}