15

As a result of another question I asked here I want to use a HashSet for my objects

I will create objects containing a string and a reference to its owner.

public class Synonym
{
   private string name;
   private Stock owner;
   public Stock(string NameSynonym, Stock stock)
   {
       name=NameSynonym;
       owner=stock
   }
   // [+ 'get' for 'name' and 'owner']
}

I understand I need a comparer , but never used it before. Should I create a separate class? like:

public class SynonymComparer : IComparer<Synonym>
{
   public int Compare(Synonym One, Synonym Two)
   { // Should I test if 'One == null'  or  'Two == null'  ???? 
       return String.Compare(One.Name, Two.Name, true); // Caseinsesitive
   }

}

I prefer to have a function (or nested class [maybe a singleton?] if required) being PART of class Synonym instead of another (independent) class. Is this possible?

About usage: As i never used this kind of thing before I suppose I must write a Find(string NameSynonym) function inside class Synonym, but how should I do that?

public class SynonymManager
{ 
    private HashSet<SynonymComparer<Synonym>> ListOfSynonyms;

    public SynonymManager()
    {
        ListOfSymnonyms = new HashSet<SynonymComparer<Synonym>>();
    }

    public void SomeFunction()
    { // Just a function to add 2 sysnonyms to 1 stock
        Stock stock = GetStock("General Motors");
        Synonym otherName = new Synonym("GM", stock);
        ListOfSynonyms.Add(otherName);
        Synonym otherName = new Synonym("Gen. Motors", stock);
        ListOfSynonyms.Add(otherName);
    }

    public Synonym Find(string NameSynomym)
    {
       return ListOfSynonyms.??????(NameSynonym);
    }
 }

In the code above I don't know how to implement the 'Find' method. How should i do that?

Any help will be appreciated (PS If my ideas about how it should be implemented are completely wrong let me know and tell me how to implement)

ilya n.
  • 18,398
  • 15
  • 71
  • 89
SoftwareTester
  • 1,048
  • 1
  • 10
  • 25

3 Answers3

22

A HashSet doesn't need a IComparer<T> - it needs an IEqualityComparer<T>, such as

public class SynonymComparer : IEqualityComparer<Synonym>      
{
   public bool Equals(Synonym one, Synonym two)
   {
        // Adjust according to requirements.
        return StringComparer.InvariantCultureIgnoreCase
                             .Equals(one.Name, two.Name);

   }

   public int GetHashCode(Synonym item)
   {
        return StringComparer.InvariantCultureIgnoreCase
                             .GetHashCode(item.Name);

   }
}

However, your current code only compiles because you're creating a set of comparers rather than a set of synonyms.

Furthermore, I don't think you really want a set at all. It seems to me that you want a dictionary or a lookup so that you can find the synonyms for a given name:

public class SynonymManager
{ 
    private readonly IDictionary<string, Synonym> synonyms = new
        Dictionary<string, Synonym>();

    private void Add(Synonym synonym)
    {
        // This will overwrite any existing synonym with the same name.
        synonyms[synonym.Name] = synonym;
    }

    public void SomeFunction()
    { 
        // Just a function to add 2 synonyms to 1 stock.
        Stock stock = GetStock("General Motors");
        Synonym otherName = new Synonym("GM", stock);
        Add(otherName);
        ListOfSynonyms.Add(otherName);
        otherName = new Synonym("Gen. Motors", stock);
        Add(otherName);
    }

    public Synonym Find(string nameSynonym)
    {
       // This will throw an exception if you don't have
       // a synonym of the right name.  Do you want that?
       return synonyms[nameSynonym];
    }
}

Note that there are some questions in the code above, about how you want it to behave in various cases. You need to work out exactly what you want it to do.

EDIT: If you want to be able to store multiple stocks for a single synonym, you effectively want a Lookup<string, Stock> - but that's immutable. You're probably best storing a Dictionary<string, List<Stock>>; a list of stocks for each string.

In terms of not throwing an error from Find, you should look at Dictionary.TryGetValue which doesn't throw an exception if the key isn't found (and also returns whether or not the key was found); the mapped value is "returned" in an out parameter.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Assuming he only needs a dictionary, (my answer also goes along those lines), I wonder how the Dictionary is implemented. Is there such a thing as HashDictionary (which uses HashSet internally to hold the keys and then the standard dictionary for lookup)? – ilya n. Jun 21 '09 at 09:10
  • At the start I define a Stock having a name (that will be first synonym for that particular stock). Then it will be possible either the user supplies his own synonym for a particular stock (will be added to the dictionary) , but it can also be a synonym (or several) will be found while reading a file (i.e. a security is defined by its ISIN but [almost] each site showing values of stocks will use a different name for it, so all those different names have to be linked to the same security (=ISIN). I do not want to throw an exception in the Find but need to know if a synonym exists and its stock – SoftwareTester Jun 21 '09 at 09:25
  • 2
    @ilya: Dictionary<,> has always used a hashtable approach - but it doesn't use a HashSet internally. Basically both types are using the same concept (hashing) but they're providing different interfaces - a set just has the concept of whether an item is in the set or not, whereas a dictionary maps a key to a value. You can easily implement a HashSet given a Dictionary by just ignoring the value - it's harder to build a Dictionary from a HashSet. – Jon Skeet Jun 21 '09 at 11:03
  • 1
    @Jon Skeet: that might be stupid, but I don't know what is set in C# then (if you answer please do it at http://stackoverflow.com/questions/1023697/what-is-the-difference-between-hashset-and-set-in-c). – ilya n. Jun 21 '09 at 11:30
1

Wouldn't it be more reasonable to scrap the Synonym class entirely and have list of synonyms to be a Dictonary (or, if there is such a thing, HashDictionary) of strings?

(I'm not very familiar with C# types, but I hope this conveys general idea)

The answer I recommend (edited, now respects the case):

    IDictionary<string, Stock>>  ListOfSynonyms = new Dictionary<string,Stock>>(); 
    IDictionary<string, string>> ListOfSynForms = new Dictionary<string,string>>(); 
    class Stock 
    {   
        ...
        Stock addSynonym(String syn) 
        {
            ListOfSynForms[syn.ToUpper()] = syn;
            return ListOfSynonyms[syn.ToUpper()] = this;
        }
        Array findSynonyms()
        {
            return ListOfSynonyms.findKeysFromValue(this).map(x => ListOfSynForms[x]);
        }
    }

    ...
    GetStock("General Motors").addSynonym('GM').addSynonym('Gen. Motors');
    ...
    try  
    {
        ... ListOfSynonyms[synonym].name ...
    }  
    catch (OutOfBounds e) 
    {
        ...
    } 
    ...
    // output everything that is synonymous to GM. This is mix of C# and Python
    ... GetStock('General Motors').findSynonyms()
    // test if there is a synonym
    if (input in ListOfSynonyms) 
    {
        ...
    }
ilya n.
  • 18,398
  • 15
  • 71
  • 89
  • I need to be able to findout what Stock is associated with each particular synonym (of cours ein this example that dosn't show, but in reality it does) – SoftwareTester Jun 21 '09 at 08:30
  • Sure, that's why I keep in both examples the dictionary ListOfSynonims. To find stock from synonim you write "ListOfSynonims[synonim]". – ilya n. Jun 21 '09 at 08:39
  • As you see, this is exactly what you wanted, but instead of .?????(synonym) you write [synonim] :) – ilya n. Jun 21 '09 at 08:41
  • OK, I see using a dictionary will avoid creating another class (and objects) as dictionary itself will store relationship between 'syn' and 'this' (= stock object) . Obviously there will/can be several strings (synonyms) referring to the same stock. But I wonder if dictionary will take care of casesensitivity or not (of course I can replace 'syn' with 'syn.ToUpper()' . I suppose the getSynonyms method is there just in case I wnat to retrieve all synonyms – SoftwareTester Jun 21 '09 at 08:54
  • I'm not very familiar with Dictionary classes in C++. But it's again easiest to just always add words to the dictionary uppercase. Well, you know that anyway. Yes, getSynonims may be something you don't need at all, which means you end with exactly the simple structure you need. – ilya n. Jun 21 '09 at 09:00
  • If synonyms must be case insensitive for the purpose of addSynonym() but output to the user has to be correct case, you can either put the pair (synonym, this) into the dictionary or go the easy (but equivalent) way and have separate CanonicalForm dictionary that would give correct form from the uppercase. – ilya n. Jun 21 '09 at 09:20
  • I see you're using try .. catch for retrieving a stock (same suggested by Jon Skeet) so it seems Dictionary will not just return 'null' if not found. I will implement the try catch – SoftwareTester Jun 21 '09 at 09:28
  • You should do try {...} catch if you're expecting the synonym to be in the dictionary. If you don't know whether the string is in the dictionary, you should just simply use if(synonym in ListOfSynonyms) {...} – ilya n. Jun 21 '09 at 09:40
0

You can always use LINQ to do the lookup:

public Synonym Find(string NameSynomym)
{
   return ListOfSynonyms.SingleOrDefault(x => x.Name == NameSynomym);
}

But, have you considered using a Dictionary instead, I believe it is better suited for extracting single members, and you can still guarantee that there are no duplicates based on the key you choose.

I am not sure that lookup time is of SingleOrDefault, but I am pretty sure it is linear (O(n)), so if lookup time is important to you, a Dictionary will provide you with O(1) lookup time.

Egil Hansen
  • 15,028
  • 8
  • 37
  • 54
  • First : this doesn't need a comparer??? Second : this Find is a memberfunction of Synonym. Is the required and what about that 'x'? – SoftwareTester Jun 21 '09 at 08:36
  • SingleOrDefault is an extension method on IEnumerable, which HashSet implements. That allows you to use SingleOrDefault (or just Single) to find the single element that matches the name. You can sort of consider "x => x.Name == NameSynomym" as your comparer. It simply stats that it takes an x (in your case, that is a Synonym from ListOfSynonyms), and if x.Name == NameSynomym is true, it returns x back to you. If it do not find any match, it will return null. – Egil Hansen Jun 21 '09 at 08:43
  • But this throws exception if more than one element with the specified condition! – nawfal Nov 25 '12 at 14:46