C# Lookup ptimisation suggestion are welcome - c#

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.

Related

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

Convert var into List<MapPdf>

I have a program that I used a variable of type List < MapPdf > that I will detach in variables when filling I would like to have the possibility to use it another time but I did not have the right to identify it again as
public static void Create(List<MapPdf> pps, Saison s, Agence agence)
{
foreach (var pelerins in grouped)
{
if (string.IsNullOrEmpty(pelerins.Key) || pelerins.Count() <= 0)
break;
if (writer.PageEvent == null)
{
writer.PageEvent = new Header_List()
{
travel = ctx.Travels.Include("Transport").First(v => v.UniqueId == pelerins.Key),
travelretour = ctx.Travels.Find(pelerins.First().UniqueIdRetour),
Agence = agence,
CountAllPelerin = pelerins.Count().ToString(),
CountFeminin = pelerins.Count(x => x.Sexe == PelerinSexe.Feminin).ToString(),
CountMasculin = pelerins.Count(x => x.Sexe == PelerinSexe.Masculin).ToString(),
NomGroupe = pelerins.First().Groupe,
NumeroDoc = writer.PageNumber
};
}
}
}
And i want to use pelerins as a List when i used in another function when it is of this declaration
I used List < MapPdf > pls = pelerins.ToList(); but it does not work
CreateFr(pls, false, cb, s);
If you are referring to var pelerins within the for-each loop and if I understand the problem, you are unable use it into another method , because var pelerins is a local variable encapsulated within the for-each loop -It does not exist outside it.
You could do the following:
//public property to retrieve pelerins
public List <MapPdf> pls new List<MapPdf>();
...
...
public static void Create(List<MapPdf> pps, Saison s, Agence agence)
{
foreach (var pelerins in grouped)
{
if (string.IsNullOrEmpty(pelerins.Key) || pelerins.Count() <= 0)
break;
if (writer.PageEvent == null)
{
//do logic
...
//store the one you are interested in, so you can use it later on
pls = pelerins.ToList();
}
}
}

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.

Is this a good way to iterate through a .NET LinkedList and remove elements?

I'm thinking of doing the following:
for(LinkedListNode<MyClass> it = myCollection.First; it != null; it = it.Next)
{
if(it.Value.removalCondition == true)
it.Value = null;
}
What I'm wondering is: if simply pointing the it.Value to null actually gets rid of it.
Setting the it.Value to null will not remove the node from the list
Here is one way:
for(LinkedListNode<MyClass> it = myCollection.First; it != null; )
{
LinkedListNode<MyClass> next = it.Next;
if(it.Value.removalCondition == true)
myCollection.Remove(it); // as a side effect it.Next == null
it = next;
}
Surely (with a linked list) you need to change the link.
Eg, if you want to remove B from the LL A-B-C, you need to change A's link to B to C.
I'll admit I'm not familiar with the .NET implementation of linked lists but hopefully that's a start for you.
You are changing the value pointed to by a LinkedListNode; beware that your list will contain a hole (empty node) now.
Instead of A - B - C you are going to have A - null - C, if you "delete" B. Is that what you want to achieve?
If you can convert to using List<> rather than LinkedList<> then you can use the RemoveAll() operation. Pass an anonymous delegate like this;
List<string> list = new List<string>()
{
"Fred","Joe","John"
};
list.RemoveAll((string val) =>
{
return (0 == val.CompareTo("Fred"));
});
All this is using Linq extensions.
If you can't convert to using a list then you can use the ToList<>() method to convert it. But you'll then have to do some clear and insertion operations. Like this;
LinkedList<string> str = new LinkedList<string>();
str.AddLast("Fred");
str.AddLast("Joe");
str.AddLast("John");
List<string> ls = str.ToList();
ls.RemoveAll((string val) => val.CompareTo("Fred") == 0);
str.Clear();
ls.ForEach((string val) => str.AddLast(val));
If all this still isn't palatable then try doing a copy of the LinkedList like this;
LinkedList<string> str = new LinkedList<string>();
str.AddLast("Fred");
str.AddLast("Joe");
str.AddLast("John");
LinkedList<string> strCopy = new LinkedList<string>(str);
str.Clear();
foreach (var val in strCopy)
{
if (0 != val.CompareTo("Fred"))
{
str.AddLast(val);
}
}
I hope that helps.
I assume something like this is required
for ( LinkedListNode<MyClass> it = myCollection.First; it != null; it = it.Next ) {
if ( it.Value.removalCondition == true ) {
if ( it.Previous != null && it.Next != null ) {
it.Next.Previous = it.Previous;
it.Previous.Next = it.Next;
} else if ( it.Previous != null )
it.Previous.Next = it.Next;
} else if ( it.Next != null )
it.Next.Previous = it.Previous;
it.Value = null;
}
}
As far as I understood You want to iterate in linkedlist with for cycle which olso contains null -s , so you can use folowing :
for (LinkedListNode<string> node = a.First; node != a.Last.Next; node = node.Next)
{
// do something here
}

Categories