views:

1169

answers:

4

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)

+1  A: 

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.
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
Sure, that's why I keep in both examples the dictionary ListOfSynonims. To find stock from synonim you write "ListOfSynonims[synonim]".
ilya n.
As you see, this is exactly what you wanted, but instead of .?????(synonym) you write [synonim] :)
ilya n.
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
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.
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.
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
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.
A: 

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
First : this doesn't need a comparer??? Second : this Find is a memberfunction of Synonym. Is the required and what about that 'x'?
SoftwareTester
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
+3  A: 

A HashSet doesn't need a IComparer<T> - it needs an IEqualityComparer<T>, e.g.

public class SynonymComparer : IEqualityComparer<Synonym>
{
   public bool Equals(Synonym one, Synonym two)
   {
        // Adjust according to requirements
        return StringComparer.InvariantCultureIgnoreCase
                             .Compare(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 sysnonyms 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
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.
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
@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
@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.
@ilya: Have just done so.
Jon Skeet
A: 

Currently in my StockManager class I have this:

 private Dictionary<string, Stock> ListOfSynonyms; // List of all synonyms for stocks 

 public Stock GetStock(string NameStock)
 {
  Stock stock = null;
  if (NameStock != null)
  {
   string NameSynonym = NameStock.Trim().ToUpper();
   try
   { // Take care of NameSynonym not being present 
    stock = ListOfSynonyms[NameSynonym];
   }
   catch
   { // KeyNotFoundException will be thrown if NameSynonym is not present
   }
  }
  return stock;
 }

 public bool AddSynonym(string NameSynonym, Stock stock)
 {
  bool bAdded = false;
  if ((NameSynonym != null) && (stock !=  null))
  {
   string Synonym = NameSynonym.Trim().ToUpper();
   if ((Synonym != "") && (GetStock(Synonym) == null))
   {
    ListOfSynonyms[Synonym] = stock;
    bAdded = true;
   }
  }
  return bAdded;
 }

I think now I only need to be able to retrieve all synonyms for a particular stock (and delete them in case the stock will be removed from the ones I want to store). As it can be that 2 stocks will be merged ( because just a few sites provide the combination ISIN-StockName so the user [or program] has to be able to merge himself afterwards) I also need to change the stock a particular synonym is referring to afterwards.

(PS I have to go now, but [of course] will look at it later. Thanks for all answers to all)

SoftwareTester
you should really just edit your original post instead of posting "a answers". stackoverflow is not a forum.
Egil Hansen
Yes, and then you should accept one of answers. It's not polite to leave a question where it's possible to accept one of the answers. In case there are several good answers, people usually select the one which is more complete, answers their question more directly or just is more to their taste.
ilya n.
As for the code, I would make some minor improvements, such as AddSynonym should really be a class function from its meaning. And you don't really need bAdded: I find it more clean to do two returns. (there were several questions on SO about whether it's better to do multiple returns or a boolean variable, and I believe for small functions the consensus was for multiple returns)
ilya n.
Also, this *isn't* your code - the casing of try/catch is wrong, for example. When you post code that isn't really what you've got, it becomes very hard to tell which errors are just in the copying, and which are in the original code.
Jon Skeet
Egil : OK, I'm new here, I will do in futureIlya : I accepted an answer (clicked on 'link')I do have a Stock.AddSynonym(string Syn). I use StockManager for managing lists (i.e. StockExchanges; Tickers [some Stocks exist at several Exchanges having different valuta]) so Stock.AddSynonym(string Syn) will call the singleton StockManager.Instance.AddSynonym(Syn, this) to store it in the dictionary. Adding it to the dictionary from Stock.AddSynonym doesn't seem right to me. Alternative could be a static dictionary in class Stock. Jon : right , code from external editor and not from program
SoftwareTester
Actually, to accept an answer you click on a checkmark below the vote count :) The way you present the classes, it is reasonable to do StockManager.AddSynonym, though I would still go for a static variable in Stock. Off-topic, there has been a lot of discussion on SO about Singleton Pattern, including how to use it best; I'm not an expert, so you better check out yourself.
ilya n.