comparing current list data with old list data - c#

I have to compare current list of data with previous list of data. In my list class i have
Id,
Comment,
Updatedby,
Updated Dt,
IsEdited
and few other fields. I am comparing like below.
foreach (var cscData in Currentcloses)
{
if (cscData.Status == ReferenceEnums.ActionStatus.EDIT)
{
if (Previoussoftcloses != null)
{
foreach (var pscData in Previouscloses)
{
if (cscData.Id == pscData.Id)
{
//my logic goes here
}
}
}
}
}
Is there any better way other than this. Just want to check.
New Code
var currentData = Currentsoftcloses
.Where(c => c.Status == ReferenceEnums.ActionStatus.EDIT);
foreach (var cscData in currentData)
{
if (Previoussoftcloses != null)
{
var previous = Previoussoftcloses
.GroupBy(item => item.Id)
.ToDictionary(chunk => chunk.Key, chunk => chunk.First());
if (previous.TryGetValue(cscData.Id, out var pscData))
{
//my logic goes here
}
} }

You can get rid of inner loop; if Previouscloses is long, your code will be faster: O(|Previoussoftcloses| + |Currentcloses|) versus O(|Previoussoftcloses| * |Currentcloses|) time complexity.
// If Previoussoftcloses == null we can do nothing, let's check for this possibility
if (Previoussoftcloses != null) {
// Dictionary is faster then List on Contains O(1) vs. O(N)
var previous = Previouscloses
.GroupBy(item => item.Id)
.ToDictionary(chunk => chunk.Key, chunk => chunk.First());
// Readability: what we are going to scan
var current = Currentcloses
.Where(c => c.Status == ReferenceEnums.ActionStatus.EDIT);
foreach (var cscData in current) {
if (previous.TryGetValue(cscData.Id, out var pscData)) {
//my logic goes here
}
}
}

Related

Efficiently select a set of objects that have multi value keys

I have a ProductVersions model which has a multi part key, a int ProductId and a int VersionNum field.
The function below takes a list of simple Dto classes that are just these 2 fields with goal of returning a set full ProductVersion objects from the database that match.
Below is a less than efficient solution. I am expecting the incoming list to only between 2 to 4 items so its not too bad but I'd like to do better.
private async Task<List<ProductVersion>?> GetProductVersionsFromDto(IList<ProductVersionDto>? productVersionDtos)
{
List<ProductVersion>? productVersions = null;
if (productVersionDtos != null)
{
foreach (ProductVersionDto dto in productVersionDtos)
{
ProductVersion? productVersion = await myPortalDBContext.ProductVersions
.Where(pv => pv.ProductId == dto.ProductId && pv.VersionNum == dto.VersionNum)
.FirstOrDefaultAsync();
if (productVersion != null)
{
if (productVersions == null) productVersions = new List<ProductVersion>();
productVersions.Add(productVersion);
}
}
}
return productVersions;
}
I had consider something like this:
private async Task<List<ProductVersion>?> GetProductVersionsFromDto(IList<ProductVersionDto>? productVersionDtos)
{
List<ProductVersion>? productVersions = null;
if (productVersionDtos != null)
{
productVersions = await myPortalDBContext.ProductVersions
.Join(productVersionDtos,
pv => new { pv.ProductId, pv.VersionNum },
pvd => new { pvd.ProductId, pvd.VersionNum },
(pv, pvd) => pv)
.ToListAsync();
}
return productVersions;
}
but at runtime fails because the Join doesn't make sense. Anyone know of way to do this more efficiently with just a single round-trip into the dbContext?
Use FilterByItems extension and then you can generate desired query:
private async Task<List<ProductVersion>?> GetProductVersionsFromDto(IList<ProductVersionDto>? productVersionDtos)
{
if (productVersionDtos == null)
return null;
var productVersions = await myPortalDBContext.ProductVersions
.FilterByItems(productVersionDtos,
(pv, dto) => pv.ProductId == dto.ProductId && pv.VersionNum == dto.VersionNum, true)
.ToListAsync();
return productVersions;
}

convert given foreach statements into a single LINQ query

How to convert given foreach statements into a single LINQ query?
foreach (SubsystemBuild img in contentsXMLObj.SubsystemBuilds)
{
foreach (BuildImage buildImage in build.Images)
{
if (buildImage.Name.Equals(img.BuildName) &&
buildImage.IsOverride == false &&
img.BaseBuildPath != null)
{
buildImage.LocalPath = img.BaseBuildPath;
}
}
}
You won't get this as a single linq statement. Since you're assigning a change to one of the sequences, you'll still need a foreach loop. But you can use linq to re-organize the code.
// items in joined are Tuple<BuildImage, string>
var joined = build.Images
.Where(b => !b.IsOverride)
.Join( contentsXMLObj.SubsystemBuilds.Where(img => img.BaseBuildPath != null),
b => b.Name,
img => img.BuildName,
(b, img) => (b, img.BaseBuildPath) );
foreach(var item in joined)
{
item.Item1.LocalPath = item.Item2;
}
Note I still prefer traditional vs query comprehension syntax, but that doesn't mean this isn't linq.
You could do something similar with SelectMany(), but neither that version nor this really look any clearer to me, and I have doubts they will run any faster.
What I might do instead is still use the nested loops, but use linq .Where() operations to handle the null filters at each level:
foreach (SubsystemBuild img in contentsXMLObj.SubsystemBuilds.Where(img => img.BaseBuildPath != null))
{
foreach (BuildImage buildImage in build.Images.Where(b => !b.IsOverride) )
{
if (buildImage.Name.Equals(img.BuildName)
{
buildImage.LocalPath = img.BaseBuildPath;
}
}
}
This is a little easier to follow, and has a chance to improve performance by more aggressively culling ineligible items.
You might do better still by pushing the inner filter into it's own list at the beginning, to avoid re-running that filter. This idea is trading a little more memory use to get faster overall execution:
var images = build.Images.Where(b => !b.IsOverride).ToList();
foreach (var img in contentsXMLObj.SubsystemBuilds.Where(img => img.BaseBuildPath != null))
{
foreach (var buildImage in images.Where(b => b.Name.Equals(img.BuildName))
{
buildImage.LocalPath = img.BaseBuildPath;
}
}
One more thing to consider is this will set the same field on the same object potentially several times. Depending on the size of each sequence and the logical relationships between them, you might do much better by inverting them. This will also let us write code where we're sure we never try to assign to the same object more than once:
var subbuilds = contentsXMLObj.SubsystemBuilds.Where(img => img.BaseBuildPath is object).ToList();
foreach(var buildImage in build.Images.Where(b =>!b.IsOverride))
{
var newPath = subbuilds.First(img => buildImage.Name.Equals(img.BuildName))?.BuildPath;
buildImage.LocalPath = newPath ?? buildImage.LocalPath;
}
Now this I like! It's much shorter, and still pretty easy to understand. And it probably also performs much better.
But as always, don't take performance assumptions for granted. It's easy to make mistakes there. If performance matters, you need to measure.
This is what i wanted...
var subsysBuild = contentsXMLObj.SubsystemBuilds
.Where(s => s.BuildName.Equals(BuildImage.Name) && s.BaseBuildPath != null && !BuildImage.IsOverride)
.FirstOrDefault();
BuildImage.LocalPath = subsysBuild != null ? subsysBuild.BaseBuildPath : BuildImage.LocalPath;
});
Here is my effort.
Iteration 1
//foreach (SubsystemBuild img in contentsXMLObj.SubsystemBuilds)
//{
foreach (BuildImage buildImage in build.Images)
{
if( buildImage.IsOverride == false && contentsXMLObj.SubsystemBuilds.LastOrDefault( img => buildImage.Name.Equals(img.BuildName) && img.BaseBuildPath != null) is var img && img != null)
{
buildImage.LocalPath = img.BaseBuildPath;
}
}
//}
Iteration 2a
ToList() may be needed for build.Images., if it's not a List.
//foreach (SubsystemBuild img in contentsXMLObj.SubsystemBuilds)
//{
// foreach (BuildImage buildImage in build.Images)
// {
build.Images.ForEach( buildImage => {
if( buildImage.IsOverride == false && contentsXMLObj.SubsystemBuilds.LastOrDefault( img => buildImage.Name.Equals(img.BuildName) && img.BaseBuildPath != null) is var img && img != null)
{
buildImage.LocalPath = img.BaseBuildPath;
}});
// }
//}
Iteration 2b
ToList() may be needed for build.Images., if it's not a List.
build.Images.ForEach(buildImage =>
buildImage.LocalPath = buildImage.IsOverride == false && contentsXMLObj.SubsystemBuilds.LastOrDefault( img => buildImage.Name.Equals(img.BuildName) && img.BaseBuildPath != null) is var img && img != null
? img.BaseBuildPath
: buildImage.LocalPath);

can not convert from System.Collection.Generic.List<Dal.Questio> to Dal.Questio

I want to append my list into another list but when i do this i got the error
How can it be solved?
public List<Questio> Get(int Id)
{
List<Questio> Quest;
Quest = null;
try
{
using (db = new Entities())
{
var Qu = db.Que.Where(x => x.Lan == Id).Select(i => i.Id);
foreach (var a in Qu)
{
var ID = db.Quess.Where(x => x.QueId == a).Select(i => i.Qued);
foreach (var j in ID)
{
var vv = db.Questio.Where(m => m.QId == j).ToList();
Quest.Add(vv);
}
}
}
}
}
You just need to use AddRange that accepts IEnumerable:
Quest.AddRange(vv);
Add is for adding a single item only.
If what you actually need is a list of lists, then declare Quest differently:
List<List<Questio>> Quest = new List<List<Questio>>();
...
var vv = db.Questio.Where(m => m.QId == j).ToList();
Quest.Add(vv);
Now your call to Add should work fine.
Two side notes, both beyond the scope of the question: 1) as the comments say, please review your naming; 2) this code could be simplified with more LINQ features, like Join and SelectMany.

How could I convert these foreach loops into a LINQ-expression?

I used ReSharper to inspect the code issues in my project and it notified me that the following loop could be converted into a LINQ-expression:
var dictionary = new Dictionary<string, string[]>
{
{ "400", new[] { "12345", "54321", "51423" } },
{ "500", new[] { "67890", "09876", "63727" } },
{ "600", new[] { "41713", "98234", "96547" } },
{ "700", new[] { "00000", "67990", "83752" } }
};
// ...
var targetValue = "41713";
foreach (string group in dictionary.Keys)
{
foreach (string name in dictionary[group])
{
if (name == targetValue)
return group;
}
}
return "User";
The loop basically checks the dictionary's values (string arrays) to see if targetValue belongs to any of them and returns the key of that array if found inside.
I tried doing the following, but clearly it just returns the value inside if its value is equivalent to targetValue.
var r = dictionary
.SelectMany(t => t.Value)
.FirstOrDefault(t => t == targetValue);
So you want to get the first key in the dictionary which string[]-value contains a given value?
var pairs = dictionary.Where(kv => kv.Value.Contains(myValue));
if (pairs.Any())
{
string group = pairs.First().Key;
}
or less readable but a little bit more efficient since it executes the query only once:
var pair = dictionary.FirstOrDefault(kv => kv.Value.Contains(myValue));
if (!pair.Equals(default(KeyValuePair<string, string[]>)))
{
string group = pair.Key;
}
last but not least another approach which is my favorite and also uses the "User"-default:
string group = dictionary.Where(kv => kv.Value.Contains(myValue))
.Select(kv=> kv.Key)
.DefaultIfEmpty("User")
.First();
var r = dictionary.FirstOrDefault(
x => x.Value.FirstOrDefault(y => y == myValue) != null);
This will also get the desired value back or null if it does not exist:
EDIT:
var result = dictionary.SkipWhile(n => !n.Value.Contains(myValue)).FirstOrDefault().Key;
//another way to get the key
//var result = dictionary.SingleOrDefault(n => n.Value.Contains(myValue)).Key;
if (result != null)
{
//do whatever with the result variable here
}

Refactor methods - pass in property name

I'm wondering if it's possible to refactor these two methods into one. The only difference is in the Select method; one uses BaselineSetNumber, the other ComparisonSetNumber.
public Set LoadBaselineSet(ObservableCollection<Set> sets)
{
using (var db = _contextFactory.GetContext())
{
var setNumber =
db.Users.Where(x => x.Login == Environment.UserName)
.Select(x => x.BaselineSetNumber).Single(); // !!! HERE
return sets.Single(x => x.SetNumber == setNumber);
}
}
public Set LoadComparisonSet(ObservableCollection<Set> sets)
{
using (var db = _contextFactory.GetContext())
{
var setNumber =
db.Users.Where(x => x.Login == Environment.UserName)
.Select(x => x.ComparisonSetNumber).Single(); // !!! HERE
return sets.Single(x => x.SetNumber == setNumber);
}
}
I'd like to have a method that I can call like LoadSet(sets, BaselineSetNumber); or LoadSet(sets, ComparisonSetNumber);
Yes, this is possible by creating a higher-order function. Your new code would look something like this:
public Set LoadBaselineSet(ObservableCollection<Set> sets)
{
return LoadSet(sets, (x) => x.BaselineSetNumber)
}
public Set LoadComparisonSet(ObservableCollection<Set> sets)
{
return LoadSet(sets, (x) => x.ComparisonSetNumber)
}
public Set LoadSet(ObservableCollection<Set> sets, Func<dbObject, Int> elementIdentity){
using (var db = _contextFactory.GetContext())
{
var setNumber =
db.Users.Where(x => x.Login == Environment.UserName)
.Select(elementIdentity).Single(); // !!! HERE
return sets.Single(x => x.SetNumber == setNumber);
}
}

Categories