convert given foreach statements into a single LINQ query - c#

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);

Related

'Expression of type 'System.String' cannot be used for parameter of type 'System.Reflection.PropertyInfo'

I have two classes - ContactCompany and inside List of ContactPeople.
The result must be - list of all contact people or a specific contact person that matches a certain criteria.
The criteria is a string and it will search all the string fields in both classes. If a ContactCompany is found , all the list of contact people will be displayed.
So Far I came up with this:
public List<ContactPersonDto> FilterContragentAndClients(string filter)
{
var contactCompanyStringProperties = typeof(ContactCompany).GetProperties().Where(prop => prop.PropertyType == filter.GetType() && prop.DeclaringType.Name != "AuditEntity`1");
var contactPersonStringProperties = typeof(ContactPerson).GetProperties().Where(prop => prop.PropertyType == filter.GetType());
var together = contactCompanyStringProperties.Concat(contactPersonStringProperties);
var allContactPersonFoundInCompany = this.contactCompanyRepository.GetAll(cc => contactCompanyStringProperties.Any
(prop => ((prop.GetValue(cc, null) == null) ? "" : prop.GetValue(cc, null).ToString().ToLower()) == filter)).SelectMany(acc => acc.ContactPeople).ToList();
var contactPersonOnItsOwn = contactPersonRepository.GetAll(cp => contactPersonStringProperties.Any
(prop => ((prop.GetValue(cp, null) == null) ? "" : prop.GetValue(cp, null).ToString().ToLower()) == filter));
var totalList = allContactPersonFoundInCompany.Concat(contactPersonOnItsOwn).Distinct().ToList().Take(100);
List<ContactPersonDto> result = new List<ContactPersonDto>();
foreach (var item in totalList)
{
result.Add(mapper.Map<ContactPersonDto>(item));
}
return result;
}
My idea was to check the property and its value, ToString() it and compare it with the criteria the user has inputted.
Just another note - I wrote the prop.Declarintype.Name in order to exclude AuditEntity properties.(Created By, Created At, etc.)
When I hit allContactPersonFoundInCompany the ToString() cannot be translated.
This is the full error I receive:
Expression of type 'System.String' cannot be used for parameter of type 'System.Reflection.PropertyInfo' of method 'Boolean Contains[PropertyInfo](System.Collections.Generic.IEnumerable`1[System.Reflection.PropertyInfo], System.Reflection.PropertyInfo)' (Parameter 'arg1')
I understood the implications of reflection , it is very slow, therefore here is my solution.
public List<ContactPersonDto> FilterContragentAndClients(string filter)
{
var contactPeopleInCompany = this.contactCompanyRepository.GetAll
(c => c.AccountablePerson.Contains(filter) |
c.Address.Contains(filter) ||
c.City.Contains(filter) ||
c.Name.Contains(filter) ||
c.Prefix.Contains(filter) ||
c.RegistrationNumber.Contains(filter)).SelectMany(c => c.ContactPeople);
var contactPerson = this.contactPersonRepository.GetAll
(cp => cp.Address.Contains(filter) ||
cp.Email.Contains(filter) ||
cp.Name.Contains(filter) ||
cp.PhoneNumber.Contains(filter) ||
cp.Prefix.Contains(filter));
var together = contactPeopleInCompany.Concat(contactPerson).Distinct().Take(100).ToList();
List<ContactPersonDto> result = new List<ContactPersonDto>();
foreach (var item in together)
{
result.Add(mapper.Map<ContactPersonDto>(item));
}
return result;
}

comparing current list data with old list data

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
}
}
}

Consolidation of an ObservableCollection based on keys

I'm just starting out so forgive me if I don't use the correct terminology. I'm trying to consolidate an ObservableCollection> by looping through and comparing one key to all the other keys in the collection. If they are the same it should then compare the matching keys values.
I don't have enough rep to post a pic.
private void CombineUDAs(ObservableCollection<Tuple<object, object>> UDAs)
{
foreach (var item in UDAs)
{
}
}
You can do this like so:
public void CombineUDAs( ObservableCollection<Tuple<object, object>> UDAs )
{
foreach ( var item in UDAs )
foreach ( var innerItem in UDAs.Where( innerItem => innerItem != innerItem && item.Item1 == innerItem.Item1 ) )
Console.WriteLine( "Value is same: {0}", item.Item2 == innerItem.Item2 );
}
Loop over each item
For each item search in the collection for items with the same “key”
Check if the “values” are equals
I'm a little rusty on my c# so my syntax is probably off, and you can probably do this more cleanly, but here's a rough idea...
Note that the inner for loop starts at the outer object index so you aren't looping over duplicate objects. Might increase performance.
public void CombineUDAs( ObservableCollection<Tuple<object, object>> UDAs )
{
for(outer=0; outer<UDAs.Count; outer++)
for (inner = outer; inner<UDAs.Count; inner++)
if(outer != inner && (UDAs[inner].item1 == UDAs[outer].item1) && (UDAs[inner].item2 == UDAs[outer].item2))
//Add to collection
}
It may be easier to just add the elements that are duplicates to a new collection. If you're navigating through the new collection frequently it may save performance depending on the size of the collection.
If you want to make the other objects blank you'll just have to inverse the if statement as needed. Probably something like:
if(outer != inner && (UDAs[inner].item1 != UDAs[outer].item1) || (UDAs[inner].item2 != UDAs[outer].item2))
Got it working the way I wanted with the help of a coworker here's the resulting code. Enumerator is the selected objects. I still need to go back to tighten the code up but the functionality is there.
_udaTuple = new ObservableCollection<Tuple<object, object>>();
var tempDictionary = new Dictionary<object, object>();
foreach (var item in Enumerator)
{
var modelObject = item as TSM.ModelObject;
if (modelObject != null)
{
var tempHash = new Hashtable();
modelObject.GetAllUserProperties(ref tempHash);
foreach (DictionaryEntry dictionaryEntry in tempHash)
{
if (tempDictionary.ContainsKey(dictionaryEntry.Key))
{
if (tempDictionary[dictionaryEntry.Key] is string && dictionaryEntry.Value is string)
{
if ((string)tempDictionary[dictionaryEntry.Key]!=(string)dictionaryEntry.Value)
{
tempDictionary[dictionaryEntry.Key] = "Values Do Not Match";
}
}
else if (tempDictionary[dictionaryEntry.Key] is double && dictionaryEntry.Value is double)
{
if ((double)tempDictionary[dictionaryEntry.Key] != (double)dictionaryEntry.Value)
{
tempDictionary[dictionaryEntry.Key] = "Values Do Not Match";
}
}
else if (tempDictionary[dictionaryEntry.Key] is int && dictionaryEntry.Value is int)
{
if ((int)tempDictionary[dictionaryEntry.Key] != (int)dictionaryEntry.Value)
{
tempDictionary[dictionaryEntry.Key] = "Values Do Not Match";
}
}
}
else
{
tempDictionary.Add(dictionaryEntry.Key, dictionaryEntry.Value);
}
}
}
}
foreach (var item in tempDictionary)
{
_udaTuple.Add(new Tuple<object, object>(item.Key, item.Value));
}

Finding all identifiers containing part of the token

I know I can get a string from resources using
Resources.GetIdentifier(token, "string", ctx.ApplicationContext.PackageName)
(sorry, this is in C#, it's part of a Xamarin.Android project).
I know that if my elements are called foo_1, foo_2, foo_3, then I can iterate and grab the strings using something like
var myList = new List<string>();
for(var i = 0; i < 4; ++i)
{
var id = AppContent.GetIdentifier(token + i.ToString(), "string", "package_name");
if (id != 0)
myList.Add(AppContext.GetString(id));
}
My issue is that my token names all begin with "posn." (the posn can denote the position of anything, so you can have "posn.left_arm" and "posn.brokenose"). I want to be able to add to the list of posn elements, so I can't really store a list of the parts after the period. I can't use a string-array for this either (specific reason means I can't do this).
Is there a way that I can use something akin to "posn.*" in the getidentifer call to return the ids?
You can use some reflection foo to get what you want. It is not pretty at all but it works. The reflection stuff is based on https://gist.github.com/atsushieno/4e66da6e492dfb6c1dd0
private List<string> _stringNames;
private IEnumerable<int> GetIdentifiers(string contains)
{
if (_stringNames == null)
{
var eass = Assembly.GetExecutingAssembly();
Func<Assembly, Type> f = ass =>
ass.GetCustomAttributes(typeof(ResourceDesignerAttribute), true)
.OfType<ResourceDesignerAttribute>()
.Where(ca => ca.IsApplication)
.Select(ca => ass.GetType(ca.FullName))
.FirstOrDefault(ty => ty != null);
var t = f(eass) ??
AppDomain.CurrentDomain.GetAssemblies().Select(ass => f(ass)).FirstOrDefault(ty => ty != null);
if (t != null)
{
var strings = t.GetNestedTypes().FirstOrDefault(n => n.Name == "String");
if (strings != null)
{
var fields = strings.GetFields();
_stringNames = new List<string>();
foreach (var field in fields)
{
_stringNames.Add(field.Name);
}
}
}
}
if (_stringNames != null)
{
var names = _stringNames.Where(s => s.Contains(contains));
foreach (var name in names)
{
yield return Resources.GetIdentifier(name, "string", ComponentName.PackageName);
}
}
}
Then somewhere in your Activity you could do:
var ids = GetIdentifiers("action").ToList();
That will give you all the String Resources, which contain the string action.

C# Lookup ptimisation suggestion are welcome

I have the code below which works for the purpose of what I need but I have an idea that it could be made faster. Please let me know if this code can be improved in any way...
The main issue is that I need to query "data" several time. I just need to make sure that there is no shortcut that I could have used instead.
data= GetData()// this return ILookup<Tuple(string, string, string),string>
foreach (var v0 in data)
{
if (v0.Key.Item3 == string.Empty)
{
//Get all related data
var tr_line = data[v0.Key];
sb.AppendLine(tr_line.First());
foreach (var v1 in data)
{
if (v1.Key.Item2 == string.Empty && v1.Key.Item1 == v0.Key.Item1)
{
var hh_line = data[v1.Key];
sb.AppendLine(hh_line.First());
foreach (var v2 in data)
{
if (v2.Key.Item1 == v0.Key.Item1 && v2.Key.Item2 != string.Empty && v2.Key.Item3 != string.Empty)
{
var hl_sl_lines = data[v2.Key].OrderByDescending(r => r);
foreach (var v3 in hl_sl_lines)
{
sb.AppendLine(v3);
}
}
}
}
}
}
}
Neater, more linq:
var data = GetData();
foreach (var v0 in data)
{
if (v0.Key.Item3 != string.Empty) continue;
//Get all related data
var tr_line = data[v0.Key];
sb.AppendLine(tr_line.First());
var hhLines = from v1 in data
where v1.Key.Item2 == string.Empty &&
v1.Key.Item1 == v0.Key.Item1
select data[v1.Key];
foreach (var hh_line in hhLines)
{
sb.AppendLine(hh_line.First());
var grouping = v0;
var enumerable = from v2 in data
where v2.Key.Item1 == grouping.Key.Item1 &&
v2.Key.Item2 != string.Empty &&
v2.Key.Item3 != string.Empty
select data[v2.Key].OrderByDescending(r => r)
into hl_sl_lines from v3 in hl_sl_lines select v3;
foreach (var v3 in enumerable)
{
sb.AppendLine(v3);
}
}
}
First of all, try to avoid using Tuple for this kind of code, because, even to you, a few months from now, this code will be incomprehensible. Make a class, or even better, an immutable struct with the correct property names. Even the fastest code is worthless if it is not maintainable.
That said, you have three nested loops that iterate the same collection. It would be plausible that a sorted collection will perform faster, as you will need to compare only with adjacent items.
Please try to explain what you are trying to accomplish, so someone would try to offer more specific help.

Categories