Efficient way to group all equal objects defined by IEqualityComparer - c#

I have a big list of certain objects that are of type Order. Furthermore, I have defined an IEqualityComparer<Order> and my goal is to efficiently create a list of lists (or groupings) where every sublist contains the objects that are equal to each other. Mathematically, I have an equivalence relation (IEqualityComparer) and want to have a list of all equivalence classes. I thought about using HashSet<Order>, Dictionary<> or Lookup<> but they dont seem to be perfect for my case. So any ideas?

I think this something like this shluod to the job:
var uniqueItems = yourRawList.Distinct(yourComparer);
var result = uniqueItems.Select(i => new List<Order>(yourRawList.Where(o =>yourComparer.Equals(i, o))));

Related

Mapping values of grouped query?

I'm new to C# and LINQ, so I'm unsure what the best practice for this would be:
I have an IEnumerable<Something>, where Something has properties A, B and C. I want to group my list of Somethings by the property A: However, I want the list of values for each group to be mapped to a different object, TargetSomething. I have a helper class, MyHelper, which has a static function converting any Something into a TargetSomething.
Therefore, I have source value / structure as follows:
IEnumerable<Something>
And target value / structure as follows:
IEnumerable<IGrouping<A, TargetSomething>>
Although, really, instead of an IGrouping I'd prefer to have a Dictionary or something, basically the equivalent of a Map in other languages.
So far, to group by keys, I used the following code:
from smth in smths group smth by smth.A into grp select grp;
Which seems pretty verbose for a simple groupBy operation, but what can I do.
Now my question is: How can I add the mapping of Something to TargetSomething using MyHelper.map(mySomething) in this LINQ query?
Also, as far as I'm aware, the IEnumerable resulting from this query is lazy so that not the entire content has to be in memory all the time - Is that a misconception? If not, will I lose this feature by adding a map somewhere?
Thank you
You can use ToLookup to return a Lookup which is a kind of multi-valued Dictionary.
A Lookup<TKey, TElement> implements IEnumerable<IGrouping<TKey,TElement>> so this should work fine for your use case.
var lookup = smths.ToLookup(smth => smth.A, HelperFunction);
The HelperFunction should have the return type TargetSomething and a single parameter of Something.
You could also use a lambda for that:
var lookup = smths.ToLookup(smth => smth.A, smth => new TargetSomething(...));

HashSet or Distinct to read distinct values of property in List<> of objects

This is in someway related to this (Getting all unique Items in a C# list) question.
The above question is talking about a simple array of values though. I have an object returned from a third party web service:
public class X
{
public Enum y {get; set;}
}
I have a List of these objects List<x> data;, about 100 records in total but variable. Now I want all the possible values in the list of the property y and I want to bind this do a CheckBoxList.DataSource (in case that makes a difference).
Hows the most efficient way to do this?
I can think of two algorithms:
var data = HashSet<Enum> hashSet = new HashSet<Enum>(xs.Select(s => s.y));
chkBoxList.DataSource = data;
Or
var data = xs.Select(s => s.y).Distinct();
chkBoxList.DataSource = data;
My gut feeling is the HashSet but I'm not 100% sure.
Open to better ideas if anyone has any idea?
If it is a one time operation - use .Distinct. If you are going to add elements again and again - use HashSet
The HashSet one, since it keeps the objects around after the hashset object has been constructed, and foreach-ing it will not require expensive operations.
On the other hand, the Distinct enumerator will likely be evaluated every time the DataSource is enumerated, and all the work of removing duplicate values will be repeated.

How to get the index of element in the List<T> in c#

I have a list who have some element like A B C D.
I want to get the index of B.
How i can get it?
I want to do this in C#.
This can be as simple as:
int index = list.IndexOf(b);
where b is the thing to find, however there is an ambiguity here over the definition of equality. By default, classes will use reference-equality. A common mistake is creating two class instances that have identical fields, and expecting them to count as equal. To do this, you should really override Equals (and ideally implement IEquatable<T> for the same T). If you override Equals, you should also override GetHashCode, and ideally the == and != operators.
Another alternative is to find the match with a predicate or comparer, which can avoid all that work
Suppose you have 4 element like A,B,C,D.
List Char = new List();
Char.Add("A");
Char.Add("B");
Char.Add("C");
Char.Add("D");
int index = Char.IndexOf("B");
so the output will be 1.(index of B)
I hope this will help you.
List(of T).IndexOf()
Simple Try this
Suppose your list's name is objList; Then
int index = objList.IndexOf("B");
Things get a little complicated when you have a list of complex types. You need to know that your list can have multiple objects with the same parameters, eg. there can be few "A" on the list. It all comes down to querying the list.
I often use, eg.
var results = (from i in people
where i.name == "Alexander" //here you put your predicate
select myList.indexOf(i));
or
var results = people.Where(p=>p.name == "Alexander"); //this gives You full objects, not indices
That way I have list of indices of all the people with name "Alexander". You can loop through it, get a single one or whatever.
Just remember that list can have multiple indices of the value you are looking for.

search arraylist by property using binarysearch

I currently have an arraylist containing classes in C#. The arraylist is filled like this:
foreach (XmlElement Path in locaties)
{
ISoundSource track = engine.AddSoundSourceFromFile(Path.InnerXml);
mixarray.Add(track);
}
then the array has many ISoundSource classes as its items. Now the thing that sets them apart in the array is their 'name' property. Later on I want to get the ISoundSource from the array by doing a search. I looked up on how to search arraylists and it is said to use a binarysearch but I don't see a way to look up an object with a certain property. How can I get the item from the array which has the name I specify?
You should probably use a Dictionary<,> as it will be much easier to maintain. Also, you should use List<> instead of ArrayList. If you must use BinarySearch, you will have to pass it a custom implementation of IComparer in order to have it use the Name property. Here's an example with a dictionary:
var dictionary = new Dictionary<string, ISoundSource>();
foreach (XmlElement Path in locaties)
{
ISoundSource track = engine.AddSoundSourceFromFile(Path.InnerXml);
mixarray.Add(track);
dictionary[track.Name] = track;
}
ISoundSource item = dictionary["MyTrackName"];
Check out the two parameter overload of BinarySearch which takes an IComparer as the second parameter - you then need to create a small class that inherits from IComparer that will compare the names of two of your Track objects, and pass an instance of this comparer into the BinarySearch.
There are many ways to do what you're asking for, and the right way depends on information that you haven't provided:
Does the Name property uniquely identify items?
Does every item have a Name?
Does the match have to be exact?
Is it important to know what order the items were originally added to the list in, i.e. the order that they appear in the source XML?
Are you trying to find items given their Name, or access them in order by their Name?
How important is it that this be efficient?
It may be that the right solution is to simply use LINQ to find an item:
ISoundSource track = mixarray
.Cast<ISoundSource>
.Where(x => x.Name == name)
.FirstOrDefault();
which will set track to the first item in the list whose name matches the value you're looking for, and to null if there's no match found. (If you use a List<ISoundSource> instead of an ArrayList, you can omit the Cast<ISoundSource> - one of many, many reasons to use List<T> over ArrayList in most cases.)
Most of the time I'll use a Dictionary<TKey, TValue> for this kind of thing, but that's because most the time the answers to those questions are yes, yes, yes, no, don't care about the order, pretty important.
For posterity, here is an alternative way to generate a dictionary using a simple Linq expression.
var dictionary = locaties
.Select(p->engine.AddSoundSourceFromFile(Path.InnerXml))
.ToDictionary(t->t.Name);
The .Select() transforms each node into an ISoundSource. When done, a collection (IEnumerable of ISoundSource) is returned. The .ToDictionary() then converts that list of ISoundSource to a Dictionary of string, ISoundSource.
This requires .NET Framework 3.5 or higher.

How to optimize this code

it has a property:
string Code
and 10 other.
common codes is list of strings(string[] )
cars a list of cars(Car[])
filteredListOfCars is List.
for (int index = 0; index < cars.Length; index++)
{
Car car = cars[index];
if (commonCodes.Contains(car.Code))
{
filteredListOfCars.Add(car);
}
}
Unfortunately this piece of methodexecutes too long.
I have about 50k records
How can I lower execution time??
The easiest optimization isto convert commonCodes from a string[] to a faster lookup structure such as a Dictionary<string,object> or a HashSet<string> if you are using .Net 3.5 or above. This will reduce the big O complexity of this loop and depending on the size of commonCodes should make this loop execute faster.
Jared has correctly pointed out that you can optimize this with a HashSet, but I would also like to point out that the entire method is unnecessary, wasting memory for the output list and making the code less clear.
You could write the entire method as:
var commonCodesLookup = new HashSet<int>(commonCodes);
var filteredCars = cars.Where(c => commonCodesLookup.Contains(c.Code));
Execution of the filteredCars filtering operation will be deferred, so that if the consumer of it only wants the first 10 elements, i.e. by using filteredCars.Take(10), then this doesn't need to build the entire list (or any list at all).
To do what you want, I would use the Linq ToLookup method to create an ILookup instead of using a dictionary. ToLookup was made especially for this type of scenario. It is basically an indexed look up on groups. You want to group your cars by Code.
var carCodeLookup = cars.ToLookup(car => car.Code);
The creation of the carCodeLookup would be slow but then you can use it for fast lookup of cars based on Code. To get your list of cars that are in your list of common codes you can do a fast lookup.
var filteredCarsQuery = commonCodes.SelectMany(code => carCodeLookup[code]);
This assumes that your list of cars does not change very often and it is your commonCodes that are dynamic between queries.
you could use the linq join command, like
var filteredListOfCars = cars.Join(commonCodes, c => c.Code, cC => cC, (car, code) => car).ToArray();
Here's an alternative to the linq options (which are also good ideas): If you're trying to do filtering quickly, I would suggest taking advantage of built in types. You could create a DataTable that has two fields, the id of the car in your array, and the code (you can add the other 10 things if they matter as well). Then you can create a DataView around it and use the filter property of that. It uses some really fast indexing internally (B-trees I believe) so you probably won't be able to beat its performance manually unless you're an algorithms whiz, which if you were, you wouldn't be asking here. It depends what you're doing and how much performance matters.
It looks like what you're really checking is whether the "code" is common, not the car. You could consider a fly weight pattern, where cars share common instances of Code objects. The code object can then have an IsCommon property and a Value property.
You can then do something to the effect of updating the used Code objects whenever the commoncodes list changes.
Now when you do your filtering you only need to check each car code's IsCommon property

Categories

Resources