search arraylist by property using binarysearch - c#

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.

Related

Filter IEnumerable<object> based on whether string property contains any string value of another List<string>

I have an IEnumerable collection of custom objects types which contain a string property in JSON format. I also have a List collection which I need to use in order to filter the IEnumerable. I want to remove all items from the IEnumerable where the string property contains any string value from the List collection. I can pull this off by creating a temporary list and doing some looping but I'm looking for a more elegant solution and I haven't been successful yet.
foreach (var faction in excludedFactions)
{
cardsVM.Cards = repository.Cards.ToList()
.RemoveAll(c => c.Factions.Contains(faction));
}
This is one thing that I've tried. I've been playing around with LINQ statements for the last two hours and I can't make it work. If anyone could point me in the right direction (a proper function for my requirements) that would be more than appreciated.
Make a HashSet<string> of excluded factions, then use this set to filter your cards:
var excludedFactionSet = new HashSet<string>(excludedFactions);
cardsVM.Cards = repository.Cards
.Where(c => !excludedFactionSet.Contains(c.Factions))
.ToList();
Note that using excludedFactions directly is also possible. However, using HashSet<string> makes Contains(f) check work in constant, rather than linear, time.

c# SortedSet how to get an element out

I am pretty new to this so forgive my noobishness here.
I am trying to edit an item in a c# sortedset if I find that the item exists. So I can use list.contains(value) and find that the value does exist in the list. But how do I get that item out of the list. Here is what I have. This gets really slow as my list size gets really big, so I'm guessing there must be a better way than this.
if (list.Contains(p))
{
Person exists = list.First(person => person.Name.Equals(line[0]));
// do something here to exists
}
else
{
// just add the person to the list
}
As of .NET Framework 4.7.2 there is TryGetValue method available for SortedSet.
For the .NET Frameworks older than the version 4.7.2:
It is not possible to get an element from SortedSet or HashSet collections (using the Contains method or somehow else). One can just get to know whether the collection contains the element. Since in order to find this element in the collection, one already uses this element (passing it to the Contains method), it can be assumed that one already has this element.
For the .NET Frameworks starting from the version 4.7.2:
See this answer.
Do you really need SortedSet which is red-black tree? If you don't need sorting, you shouldn't use it. Have you considered HashSet or Dictionary instead which is more suitable (fast) for getting item by key?
In your case you probably need to create Dictionary instance with key equals to person name, i.e.:
Dictionary<string, Person> list;
Then you can get person by it's name, complexity is O(1)
if(list.ContainsKey(line[0]))
{
list[line[0]]...
}
or even better:
Person p;
if(list.TryGetValue(line[0], out p))
{
p...
)
You may want to consider using the PowerCollections project — it has a lot of useful improvements to the standard generic collections.

Best way for constructing a unique list of objects in C#

I'm wondering whether it will be quicker to follow one pattern or another for constructing a unique list of objects in C#:
Option 1
Add all the items into a generic list
Call the list.Distinct function on it
Option 2
Iterate over each item
Check whether the item already exists in the list and if not add it
You can use HashSet<T>:
The HashSet class provides high-performance set operations. A set
is a collection that contains no duplicate elements, and whose
elements are in no particular order.
You can provide custom IEqualityComparer<T> via constructor.
This is one of those "should I use a shoe or a brick to pound a nail into the wood" questions. You should use the appropriate data structure for the job, which based on your requirement of "constructing a unique list of objects", the HashSet<T> class satisfies.
If you require the items in list format, you can always call ToList() on the set.
If you are concerned about the performance of looking up unique items, use a Dictionary<TKey, TVale>. Also, a dictionary requires unique keys, so you will never have duplicates.

Is there an data type that acts both as a list and a dictionary?

Normally when I need to have a list of ints/strings/etc. I create a list like:
var list = new List<string>
And then I create a hashtable that contains all the strings, and I don't insert into the list unless it isn't in the hashtable i.e. to enforce unique items in the list.
Is there a datatype that can satisfy both of these requirements for me?
There is. Use HashSet:
var set = new HashSet<int>();
set.Add(4);
set.Add(4); // there is already such an element in the set, no new elements added
Keep in mind, though, that it does not guarantee you the order of elements.
Do you just mean HashSet<string> ?
All elements in a HashSet<T> are unique; the Add() method returns a bool to indicate if a new item was actually added, or whether it was a no-op.
Is there a datatype that can satisfy both of these requirements for me?
No. A hashtable will provide you a direct access to an element given its unique key, whereas in a list you don't need a key and you could definitely have duplicates.
You can use the HashSet<T> data type MSDN. Which will only allow you to have a single copy of each value.
If you are after a set of unique values only (and don't subsequently care about ordering) then you should look at a HashSet<T>
Technically, there is System.Collections.Specialized.OrderedDictionary. However, this is an old non-updated (non-generic) class and I would generally recommend avoiding it ;-)
Represents a collection of key/value pairs that are accessible by the key or index.
In practice I would create a minimal wrapper class that exposes the required operations. (I would likely use a HashSet<T> (for existence) and a List<T> (for ordering), although just a single List<T> is far than sufficient for a relatively small n in most cases -- remember Big-O is about limits.)
Happy coding.
HashSet<string> set = new HashSet<string>();
bool inserted = set.Add("Item");
bool insertedDuplicate = set.Add("Item");
inserted.Dump("Result1");
insertedDuplicate.Dump("Result2");
//Result
//Result1 = true
//Result2 = false
You can run this in LinqPad to see the functionality and how it works.

Changing the Order in a .NET Generic Dictionary

I have a class that inherits from a generic dictionary as follows:
Class myClass : System.Collections.Generic.Dictionary<int, Object>
I have added a list of values to this in a particular order, but I now wish to change that order. Is there any way (without removing and re-adding) that I could effectively re-index the values; so change the object at index 1 to now be at index 10 for example? For example, this doesn't work:
myClass[1].Index = 10;
Have a look at the SortedDictionary class where you can sort your items by the key.
Normally, you cannot change the index of items in Dictionary because it is against the principle ;-)
Dictionaries by themselves don't have an index order. Consider inheriting from the KeyedCollection class instead. It's a merge of a dictionary and an ordinary list, and it's designed to use a member of your items as the key, and have an index order.
Dictionaries (or more generally hashtables) do not have indicies. This goes for all languages.
For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair structure representing a value and its key. The order in which the items are returned is undefined.
Taken from MSDN.
Old topic, but nevertheless the answers are not all valid
You can use Linq to achieve this.
Dictionary<int, string> Breadcrumbs = new Dictionary<int, string>();
Breadcrumbs.Add(1, "Test1");
Breadcrumbs.Add(2, "Test2");
Breadcrumbs.Add(3, "Test3");
Breadcrumbs.Add(4, "Test4");
Breadcrumbs.Add(5, "Test5");
var q = Breadcrumbs.OrderByDescending(x => x.Key);
// And bind it
gridView.DataSource = q;
gridView.DataBind();
Take a look at this question. Not a duplicate but I think it fully applies to you. They suggest you have two collections: both a dictionary (for lookups) and a List (for keeping an order that isn't sortable). If these are reference types, then you shouldn't be too concerned about memory usage here since you're only duplicating pointers (unless, of course, you have a bajillion items in here).
Dictionary<> is an unordered collection, you'll only get the elements out of it in the order you put them in by accident. You'll need a SortedDictionary<>.
I have added a list of values to this in a particular order
There is no such thing like particular order for Dictionary.
Perhaps you can look at SortedList. It supports indexing for keys and values but adding / removal are O(n) complexity vs O (log N) for SortedDictionary.

Categories

Resources