There must be a way to compare two sets of results while staying in LINQ. Here's my existing code that uses a HashSet to do the comparison after two separate queries:
public static void AssertDealershipsShareTransactionGatewayCredentialIds(long DealershipLocationId1,
long DealershipLocationId2)
{
using (var sqlDatabase = new SqlDatabaseConnection())
{
var DealershipCredentials1 =
sqlDatabase.Tables.DealershipLocationTransactionGateway
.Where(x => x.DealershipLocationId == DealershipLocationId1)
.Select(x => x.TransactionGatewayCredentialId);
var DealershipCredentials2 =
sqlDatabase.Tables.DealershipLocationTransactionGateway
.Where(x => x.DealershipLocationId == DealershipLocationId2)
.Select(x => x.TransactionGatewayCredentialId);
var doSetsOfCredentialsMatch = new HashSet<int>(DealershipCredentials1).SetEquals(DealershipCredentials2);
Assert.IsTrue(doSetsOfCredentialsMatch,
"The sets of TransactionGatewayCredentialIds belonging to each Dealership did not match");
}
}
Ideas? Thanks.
Easy answer (This will make 1, possibly 2 database calls, both of which only return a boolean):
if (list1.Except(list2).Any() || list2.Except(list1).Any())
{
... They did not match ...
}
Better answer (This will make 1 database call returning a boolean):
var DealershipCredentials1 =
sqlDatabase.Tables.DealershipLocationTransactionGateway
.Where(x => x.DealershipLocationId == DealershipLocationId1)
.Select(x => x.TransactionGatewayCredentialId);
var DealershipCredentials2 =
sqlDatabase.Tables.DealershipLocationTransactionGateway
.Where(x => x.DealershipLocationId == DealershipLocationId2)
.Select(x => x.TransactionGatewayCredentialId);
if (DealershipCredentials1.GroupJoin(DealershipCredential2,a=>a,b=>b,(a,b)=>!b.Any())
.Union(
DealershipCredentials2.GroupJoin(DealershipCredential1,a=>a,b=>b,(a,b)=>!b.Any())
).Any(a=>a))
{
... They did not match ...
}
The second method works by unioning a left outer join that returns a boolean indicating if any unmatching records were found with a right outer join that does the same. I haven't tested it, but in theory, it should return a simple boolean from the database.
Another approach, which is essentially the same as the first, but wrapped in a single LINQ, so it will always only make 1 database call:
if (list1.Except(list2).Union(list2.Except(list1)).Any())
{
}
And another approach:
var common=list1.Intersect(list2);
if (list1.Except(common).Union(list2.Except(common)).Any()) {}
Related
I'm trying to use a custom method for ordering but I also want to use that same custom method to only return results that match a certain value. I realize that the code below works but I was hoping there was a way to combine both methods to hopefully speed up the process.
public IEnumerable<List<decimal>> GetBestList(List<List<decimal>> inputList)
{
var bestList = inputList.Where(x => x != null && CalculateAverage(x) > 0).
OrderByDescending(x => CalculateAverage(x)));
return bestList;
}
public decimal CalculateAverage(List<decimal> inputList)
{
return inputList.Average();
}
As far as I understand you want to prevent recalculation of average, so you can use Select to create a temporary tuple containing average and original list, for example like that:
public IEnumerable<List<decimal>> GetBestList(List<List<decimal>> inputList)
{
var bestList = inputList
.Where(x => x != null )
.Select(x => (x, Avg: CalculateAverage(x)))
.Where(x => x.Avg > 0)
.OrderByDescending(x => x.Avg)
.Select(x => x.x);
return bestList;
}
The way to avoid performing the potentially expensive computation multiple times is to project the sequence into a new value that includes the list and the computation. This is simpler and easier with query syntax than method syntax:
public IEnumerable<List<decimal>> GetBestList(List<List<decimal>> inputList)
{
var query = from list in inputList
where list != null
let average = CalculateAverage(list)
where average > 0
orderby average
select list;
}
I call dynamically sort rows of a table when the orderby column is in the parent table doing the following...
public List<ServiceRequest> SortSRsByParentFields(string p_Criteria,
bool p_sortDescending,
bool p_ShowAll = true) {
var propertyInfo = typeof(ServiceRequest).GetProperty(p_Criteria);
var sortedList1 = new List<ServiceRequest>();
var sortedList2 = new List<ServiceRequest>();
var myServiceRequests = GetMyServiceRequests();
var otherServiceRequests = GetOthersServiceRequests();
if (p_sortDescending)
{
sortedList1 = myServiceRequests
.AsEnumerable()
.OrderByDescending(x => propertyInfo.GetValue(x, null)).ToList();
sortedList2 = otherServiceRequests.AsEnumerable()
.OrderByDescending(x => propertyInfo.GetValue(x, null))
.ThenBy(x => x.Client.LastNameFirst).ToList();
}
else
{
sortedList1 = myServiceRequests.AsEnumerable()
.OrderBy(x => propertyInfo.GetValue(x, null)).ToList();
sortedList2 = otherServiceRequests.AsEnumerable()
.OrderBy(x => propertyInfo.GetValue(x, null))
.ThenBy(x => x.Client.LastNameFirst).ToList();
}
var allSRs = p_ShowAll == false ? sortedList1.Concat(sortedList2).Take(1000)
.ToList() : sortedList1.Concat(sortedList2).ToList();
return allSRs;
}
But I can't seem to make this method work if the orderby column is in a child table (a table related to the parent though an FKey).
So the question is how do I make that work?
EF isn't really designed with dynamic sorting in mind. But there are alternatives you can use for cases like this without replacing the rest of your EF code.
For example, with Tortuga Chain you can write:
ds.From("ServiceRequests", [filter]).WithSorting (new SortExpression(p_Criteria, p_sortDescending)).ToCollection<ServiceRequest>().Execute();
You can also just generate SQL directly, but I don't recommend that approach because you have to carefully check the sort expression to ensure it is actually a column name and not a SQL injection attack.
I would like to use linq to check if one of multiple logic statements return true.
So far I've tried the following:
winningCombinations = new[,]
{
{1,2,3},
{4,5,6},
{7,8,9},
{1,4,7},
{1,4,7},
{3,6,9},
{1,5,9},
{3,5,7}
};
if (Enumerable.Range(0,7).Where(x => this.contentOf(winningCombinations[x,0]) == this.contentOf(winningCombinations[x,1]) &&
this.contentOf(winningCombinations[x,1]) == this.contentOf(winningCombinations[x,2]))
.Select(x => true).FirstOrDefault(x => x))
{
_isDone = true;
_game.wonBy(_turnOf);
}
Basically contentOf() require an Index as a parameter and return a char value.
My question is, is there a way to make my code work? I'd like to use the coordinates contained in "winningCombinations" to check if there is a winningCombination on my grid (3 identical characters), preferably using Linq.
I'm fairly unclear on what you're trying for, but it sounds like IEnumerable.Any should do what you want.
if (Enumerable.Range(0,7)
.Any(x => this.contentOf(winningCombinations[x,0]) == this.contentOf(winningCombinations[x,1]) &&
this.contentOf(winningCombinations[x,1]) == this.contentOf(winningCombinations[x,2]))
{
_isDone = true;
_game.wonBy(_turnOf);
}
This is pretty self-explanatory, but essentially the goal is to find any sets for which the result it true. If it finds one, it returns true and jumps into the if block. If it looks through the full array and doesn't find any matches, it will return false.
If you're looking for even more LINQ, and who isn't, this is arguably cleaner. Arguably.
if (Enumerable.Range(0, winningCombinations.GetLength(0))
.Select(c => Enumerable.Range(0, winningCombinations.GetLength(1)
.Select(x => this.contentOf(winningCombinations[c, x])
.Any(x => !x.Distinct().Skip(1).Any())
{
_isDone = true;
_game.wonBy(_turnOf);
}
Broken up a bit,
// Get the indexes for of the array
var ys = Enumerable.Range(0, winningCombinations.GetLength(0));
var xs = Enumerable.Range(0, winningCombinations.GetLength(1));
// Select the values at each index, becomes a collection of collections
var vals = ys.Select(y => xs.Select(x => this.contentOf(winningCombinations[y, x]));
// Discover whether at least one of the sets has exactly one value contained.
return vals.Any(c => !x.Distinct().Skip(1).Any())
I am using the query below to grab all records that have a SubCategoryName == subCatName and i want to return all of there ProductID's as a list of ints. Problem is when my code runs it is only returning 1 int(record) instead of all. How can i make it return all of the records that have that subCatName? Its returning a count = 1 with a capacity of 4. So it is a int[4] but only the first [0] is = to a actual product ID the rest returning zero?
public List<int> GetPRodSubCats(string subCatName)
{
var _db = new ProductContext();
if (subCatName != null)
{
var query = _db.ProductSubCat
.Where(x => x.SubCategory.SubCategoryName == subCatName)
.Select(p => p.ProductID);
return query.ToList<int>();
}
return null;
}
As Daniel already has mentioned, the code should work. But maybe you are expecting that it's case-insensitive or ignores white-spaces. So this is more tolerant:
subCatName = subCatName.Trim();
List<int> productIDs = _db.ProductSubCat
.Where(x => String.Equals(x.SubCategory.SubCategoryName.Trim(), subCatName, StringComparison.OrdinalIgnoreCase))
.Select(p => p.ProductID)
.ToList();
This seems more like an expected behavior here. How do you know you don't only have 1 record that satisfies the Where predicate.
Your code is correct, however you might want to normalize your comparison.
x => x.SubCategory.SubCategoryName == subCatName
to use a specific case for instance:
x => x.SubCategory.SubCategoryName.ToLower() == subCatName.ToLower()
you might also consider a Trim.
The following statement is not returning distinct values, but the whole list:
public ObservableCollection<MasterPartsList> ParentAssemblyBOM
{
get
{
var enumerable = this._parentAssemblyBOM
.Where(parent => parent.isAssy == true).Distinct();
return new ObservableCollection<MasterPartsList>(enumerable) ;
}
Truly, I should only be able to tell that the object is unique because this._parentAssemblyBOM.partNumber would be the distinct property. How do I work in this logic to yield the correct results?
Thanks in advance!
Try grouping by the identifier (in your case part number) and then select the first of the group:
var enumerable = this._parentAssemblyBOM
.Where(parent => parent.isAssy == true)
.GroupBy(x => x.partNumber)
.Select(x => x.FirstOrDefault());