c# Linq - Check if composite key exists in another list - c#

I have a list of errors defined as the following:
List<Errors> test1 = new List<Errors>();
public class Errors
{
public int ID {get; set;}
public int Occurrence {get; set;}
//.....
//.....
}
The errors are unique by the combination of the two fields above.
A second list keeps track of whose been assigned to the errors.
List<Tasks> test2 = new List<Tasks>();
public class Tasks
{
public int ID {get; set;}
public int Occurrence {get; set;}
public int EmployeeID {get; set;}
//.....
}
Also made unique by the same two fields. Essentially the tasks are a subset of the errors that have been assigned to someone.
I would like to use a LINQ query (or equivalent) to determine if the composite ID from the List<Errors> exists in List<Tasks>... To be clear it must use both IDS.
I have found the below solution but have not been able to adopt it to a composite key.
`var test2NotInTest1 = test2.Where(t2 => !test1.Any(t1 => t2.Contains(t1)));`

Just need to use and && operator and check both properties instead of one:
var test2NotInTest1 = test2.Where(t2 => !test1.Any(t1 => t1.ID == t2.ID && t1.Occurance == t2.Occurance);

There is a function for that... Except
var test2NotInTest1 = test1.Except(test2);
If you don't have it you will need to create the interface for equal -- something like this:
var test2NotInTest1 = test1.Except(test2, new ErrorsComparer());
class ErrorsComparer : IEqualityComparer<Errors>
{
public bool Equals(Errors x, Errors 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;
//Check whether the products' properties are equal.
return x.ID == y.ID && x.Occurrence == y.Occurrence;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Errors e)
{
if (Object.ReferenceEquals(e, null)) return 0;
int hashID = e.ID == null ? 0 : e.ID.GetHashCode();
int hashO = e.Occurrence.GetHashCode();
//Calculate the hash code for the product.
return hashID ^ hashO;
}
}

You were almost there, just add a correct condition to the LINQ expression:
var test2NotInTest1 = listOfErrors.Where(e => !listOfTasks.Any(t => t.ID == e.Id && t.Occurrence == e.Occurrence)).ToList();

For: to determine if the composite ID from the Errors exists in Tasks...
Another approach is to use Enumerable.Join Method
var assignedErrors =
errors.Join(tasks,
error => new { Id = error.Id, Occurrence = error.Occurrence },
task => new { Id = task.Id, Occurrence = task.Occurrence },
(error, task) => error);
For: to determine if the composite ID from the Errors not exists in Tasks..., as in your sample:
var test2NotInTest1 = test2.Where(t2 => !test1.Any(t1 => t2.Contains(t1)));
You can use HashSet to "speed up" search for already assigned errors.
var assignedErrors = tasks.Select(task => (task.Id, task.Occurrence)).ToHashSet();
var notAssignedErrors =
errors.Where(error => assignedErrors.Contains((error.Id, error.Occurrence)) == false)
.ToList();
Or create your own domain specific extension method:
public static IEnumerable<Errors> NotAssignedIn(
this IEnumerable<Errors> errors,
IEnumerable<Tasks> tasks)
{
var assigned = new HashSet<(int Id, int Occurrence)>();
foreach (var task in tasks)
{
assigned.Add((task.Id, task.Occurrence));
}
foreach (var error in errors)
{
if (assigned.Contains((error.Id, error.Occurrence)) == false)
{
yield return error;
}
}
}
Usage:
var notAssignedErrors = errors.NotAssignedIn(tasks);

Related

Linq Join multi key one side of key is null

Trying to join two tables where part of the key on one side is nullable. Getting "Type arguments cannot be inferred from the usage", which I believe is related to the mismatched types in the key.
TableA.GroupJoin(TableB,
a => new {a.IntKeyA, a.StringKeyA},
b => new {b.NullIntKeyB, b.StringKeyB}
(tabA, tabB) => new {tabA, tabB});
Tried to cast the type of the key in TableA
a => new (int?, string) {a.IntKeyA, a.StringKeyA}
or
a => (int?, string)(new {a.IntKeyA, a.StringKeyA})
Tried to Coalesce the key in TableB, magic number 0 isn't great but would have worked in this scenario.
b => new {b.NullIntKeyB ?? 0, b.StringKeyB}
Tried GetValueOrDefault
b => new {b.NullIntKeyB.GetValueOrDefault(), b.StringKeyB}
I suppose I could probably define a class to hold the key but I don't really want to do that every time this issue comes up.
For the time being, this seems to have worked, but I'm not going to mark it as the answer yet in hopes there's an easier way.
class ReportKey
{
private int? IntKey { get; }
private string StringKey { get; } = string.Empty;
internal ReportKey(int? intKey, string stringKey)
{
IntKey = intKey;
StringKey = stringKey;
}
public override bool Equals(object obj)
{
var item = obj as ReportKey;
if (item == null) return false;
return this.IntKey == item.Intkey &&
StringKey == item.StringKey;
}
public override int GetHashCode()
{
return $"{IntKey}{StringKey}".GetHashCode();
}
}
...
TableA.GroupJoin(TableB,
a => new ReportKey(a.IntKeyA, a.StringKeyA),
b => new ReportKey(b.NullIntKeyB, b.StringKeyB),
(tabA, tabB) => new {tabA, tabB});

How to find by unique index in EF Core

public class EmployerRestaurant
{
public int Id { get; set; }
public int EmployerId { get; set; }
public int RestaurantId { get; set; }
public bool IsPrimary { get; set; }
}
// configuration
builder.HasIndex(i => new {i.EmployerId, i.RestaurantId }).IsUnique();
Is it possible to perform a search on this table providing an array of unique indexes?
Something like
_context.EmployerRestaurant.Find({1,2},{2,2});
Find is just a shorthand for DbSet.SingleOrDefault(), so you can just use it by providing a key as parameter.
The simplest way to get what you want is to filter the DbSet using a where statement:
_context.EmployerRestaurant.Where(e => (e.EmployerId == 1 && e.RestaurantId == 2) || e.EmployerId == 2 && e.RestaurantId == 2);
EF Core will translate the expression and the database will do the rest (using the index you provided).
As I suppose, if you don't know what ids to get at compile time, you can provide to Where() a dynamically generated expression of type Expression<Func<EmployerRestaurant, bool>> to get the same result:
List<(int empId, int RestId)>? selectedEmps = new List<(int empId, int RestId)>()
{
(1,2), (2,2)
};
//init an empty expression that compute to false because it's an Or operation
Expression<Func<EmployerRestaurant, bool>> predicateExpr = (e) => false;
foreach ((int empId, int RestId) in selectedEmps)
{
//concat the real condition for each item in the array to the "dummy" expression.
//The resulting expression will contains all your tuples linked in a big Or expression
Expression<Func<EmployerRestaurant, bool>> itemExpression = (e) => e.EmployerId == empId && e.RestaurantId == RestId;
InvocationExpression tmpExpr = Expression.Invoke(itemExpression, predicateExpr.Parameters.Cast<Expression>());
predicateExpr = Expression.Lambda<Func<EmployerRestaurant, bool>>(Expression.OrElse(predicateExpr.Body, tmpExpr), predicateExpr.Parameters);
}
//provide your generated predicate to EF Core and get the result
List<EmployerRestaurant>? emps = db.EmployerRestaurant.Where(predicateExpr).ToList();
To easily compose your query expression you can also use Joseph Albahari's PredicateBuilder.
Using the PredicateBuilder the foreach code will be like:
Expression<Func<EmployerRestaurant, bool>>? predicateExpr = PredicateBuilder.False<EmployerRestaurant>();
foreach ((int empId, int RestId) in selectedEmps)
{
predicateExpr = predicateExpr.Or((e) => e.EmployerId == empId && e.RestaurantId == RestId);
}
Have not tested it, but tuples may help ...
List<(int employerId, int restaurantId)> _idsToSearchFor = new()
{
(1, 2)
(2, 2)
};
List<EmployerRestaurant> matches = _context.EmployerRestaurant
.Where(er => _idsToSearchFor.Contains((er.EmployerId, er.RestaurantId))
.ToList();

Compare 2 not identical DTO but have common properties in Fluent Assertion

I am writing a unit test for a manual mapper. It maps an object to two different classes but have common properties. how to compare if their properties are equal in fluent assertion?
This is what I tried
var domain = new Domain.ConsentDefinition()
{
SomeProperty = 1,
ListOfFirstDTO = new List<FirstDTO>()
{
new FirstDTO()
{
Name = "Label",
Age = 18,
}
},
SomeOtherProperty = "one"
}
ef = domain.ToEF();
domain.SomeProperty.Should().Be(ef.SomeProperty);
domain.SomeOtherProperty.Should().Be(ef.SomeOtherProperty);
domain.ListFirstDTO.Should().Equal(ef.ListOfSecondDTO); // This is NOT working
classes
public class FirstDTO
{
public string Name {get;set;}
public int Age {get;set;}
}
public class SecondDTO
{
public string Name {get;set;}
public int Age {get;set;}
public string Email {get;set;}
}
Override firstDTO's equals so you compare values instead of references:
public override bool Equals(object obj)
{
if (obj == null || !(obj is FirstDTO) || !(obj is SecondDTO))
{
return false;
}
if(obj is SecondDTO){
return (this.Name == ((SecondDTO)obj).Name)
&& (this.Age == ((SecondDTO)obj).Age)
}
// if obj is instance of FirstDTO check the rest of fields...
}
and run again
domain.ListFirstDTO.Should().Equal(ef.ListOfSecondDTO); // This is NOT working
Another more elegant solution with no need of overriding equals would be
domain.ListFirstDTO.Select(c => c.Name).Should().Equal(ef.ListOfSecondDTO.Select(c => c.Name);
domain.ListFirstDTO.Select(c => c.Age).Should().Equal(ef.ListOfSecondDTO.Select(c => c.Age);
fluentassertion/collections
domain.Should().BeEquivalentTo(new
{
SomeProperty = ef.SomeProperty,
SomeOtherProperty = ef.SomeOtherProperty,
ListFirstDTO = ef.ListOfSecondDTO
});
or
domain.Should().BeEquivalentTo(ef, options => options
.Including(x => x.SomeProperty)
.Including(x => x.SomeOtherProperty)
.Including(x => x.ListOfSecondDTO));
By default, FA will compare two collections by ignoring the order of the items in the collection. Use WithStrictOrdering to control that.
If the second DTO implements Equals, then FA will use that. You can override that by using the ComparingByMembers<T> option.

Best Comparison Algorithm using Entity Framework

I was wondering what was the best approach to compare multiple objects that are created and having the state of the objects changed to Inactive (Deleted), while creating history and dependencies.
This also means im comparing past and present objects inside a relational table (MarketCookies).
Id | CookieID | MarketID
The ugly solution i found was calculating how many objects had i changed.
For this purpose lets call the items of the Past: ListP
And the new items: ListF
I divided this method into three steps:
1 - Count both lists;
2 - Find the objects of ListP that are not present in List F and change their state to Inactive and update them;
3 - Create the new Objects and save them.
But this code is very difficult to maintain.. How can i make an easy code to maintain and keep the functionality?
Market Modal:
public class Market()
{
public ICollection<Cookie> Cookies {get; set;}
}
Cookie Modal:
public class Cookie()
{
public int Id {get;set;}
//Foreign Key
public int CookieID {get;set}
//Foreign Key
public int MarketID {get;set;}
}
Code:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
int ListPCount = ListP.Count();
int ListFCount = ListF.Count();
if(ListPCount > ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete the Object
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Object
}
});
}
else if(ListPCount < ListFCount)
{
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
}
else if(ListPCount == ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
}
}
Without a good, minimal, complete code example that clearly illustrates the question, it's hard to know for sure what even a good implementation would look like, never mind "the best". But, based on your description, it seems like the LINQ Except() method would actually serve your needs reasonably well. For example:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
foreach (var item in ListP.Except(ListF))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP))
{
// create new object
}
}
This of course assumes that your objects have overridden Equals() and GetHashCode(). If not, you can provide your own implementation of IEqualityComparer<T> for the above. For example:
// General-purpose equality comparer implementation for convenience.
// Rather than declaring a new class for each time you want an
// IEqualityComparer<T>, just pass this class appropriate delegates
// to define the actual implementation desired.
class GeneralEqualityComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _equals;
private readonly Func<T, int> _getHashCode;
public GeneralEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
_equals = equals;
_getHashCode = getHashCode;
}
public bool Equals(T t1, T t2)
{
return _equals(t1, t2);
}
public int GetHashCode(T t)
{
return _getHashCode(t);
}
}
Used like this:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
IEqualityComparer<Cookie> comparer = new GeneralEqualityComparer<Cookie>(
(t1, t2) => t1.Id == t2.Id, t => t.Id.GetHashCode());
foreach (var item in ListP.Except(ListF, comparer))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP, comparer))
{
// create new object
}
}

Comparing two classes in LINQ - getting 'mismatches'

I have the following class:
public class DocumentCompare
{
public string Customer;
public string Filename;
public string Reference;
public DateTime? Date;
public override bool Equals(object obj)
{
if (obj == null)
return false;
DocumentCompare doc = obj as DocumentCompare;
if ((Object)doc == null)
return false;
return (doc.Customer == Customer) && (doc.Date == Date) && (doc.Filename == Filename) && (doc.Reference == Reference);
}
public bool Equals(DocumentCompare doc)
{
if ((object)doc == null)
return false;
return (doc.Customer == Customer) && (doc.Date == Date) && (doc.Filename == Filename) && (doc.Reference == Reference);
}
public override int GetHashCode()
{
return string.Format("{0}_{1}_{2}_{3}",Customer,Filename,Reference,(Date == null ? "" : Date.Value.ToString())).GetHashCode();
}
}
I will be retrieving 2 lists of this class - what I want to do is to compare the two, and get ones that don't exist in both. So if an item exists in x list but not in y, I want to perform an action for the items in this list. If an item exists in y list but not in x, I want to do a different action.
How would I do this? Using LINQ I guess!
EDIT: Performance is not much of an issue - this will only be run once
It sounds like you just want Except:
foreach (var newItem in firstList.Except(secondList))
{
...
}
As an aside:
That's not a terribly nice way of generating a hash code - search for other questions here.
Delegate from Equals(object) to Equals(DocumentCompare) to avoid repetitive logic
Mutable types aren't great candidates for equality comparisons (in particular, one you've used a value as a key in a dictionary, if you change the equality-sensitive components you won't be able to find the key again)
Even if you do want it to be mutable, properties are better for encapsulation than public fields
I would either seal the type or check whether the two objects are exactly the same type, as otherwise you could end up with asymmetric equality
here is the code:
var elementsMissingFromFirstList = firstList.Except(secondList).ToList();
var elementsMissingInSecondList = secondList.Except(firstList).ToList();
now you can perform your actions on these missing elements :)
You can use this method to compare objects of two different Lists. exmp: List and List x and y = DocumentCompare,
public static bool EqualsObject<T>(this T t1, T t2) where T : class
{
var p1 = t1.GetType().Fields();
var p2 = t2.GetType().Fields();
for (int j = 0; j < p1.Length; j++)
{
var x = p1[j].GetValue(t1, null);
var y = p2[j].GetValue(t2, null);
if (x == null && y == null)
continue;
if (x != null && y == null)
return false;
if (x == null)
return false;
if (!x.Equals(y))
{
return false;
}
}
return true;
}
This method will show the difference between these two lists.
public static List<T> DifferentObjects<T>(List<T> t, List<T> t2) where T : class
{
var diff = new List<T>();
if (t != null && t2 != null)
{
foreach (T t1 in t)
{
var state = false;
foreach (T t3 in t2.Where(t3 => EqualsObject(t1,t3)))
{
state = true;
}
if (!state)
{
diff.Add(t1);
}
}
}
return diff;
}
you can use code this way
var t = new List<DocumentCompare>();
var t2 = new List<DocumentCompare>();
t.Add(new DocumentCompare{Customer = "x"});
t.Add(new DocumentCompare{Customer = "y"});
t.Add(new DocumentCompare{Customer = "z"});
t2.Add(new DocumentCompare { Customer = "t" });
t2.Add(new DocumentCompare { Customer = "y" });
t2.Add(new DocumentCompare { Customer = "z" });
var list = DifferentObjects(t, t2);
var list2 = DifferentObjects(t2, t);
you used fields (Customer,FileName etc..) in your class, so that GetType().Fields(); is used in EqualsObject method. if you use property , you should use GetType().Properties(); in EqualsObject method.

Categories

Resources