A Generic HashSet in .NET 2.0
April 2nd, 2009
One of the great new additions to .NET 3.5 is HashSet<T>. Unfortunately, you can’t run the 3.5 framework with Win2K, which is what my users are forced to use until we get a company-wide upgrade to XP or higher. We get to develop in VS 2008 on XP, and use some of the new C# 3.0 language features, but we still have to compile to 2.0, and because the users can’t run System.Core on Win2K, that means LINQ and HashSet<T> are out, for now.
Or so I thought, until I figured out a way to write it myself using a Dictionary<T, object> as the internal storage. Feel free to use it as you wish:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
public class HashSet<T> : ICollection<T>, ISerializable, IDeserializationCallback
{
private readonly Dictionary<T, object> dict;
public HashSet()
{
dict = new Dictionary<T, object>();
}
public HashSet(IEnumerable<T> items) : this()
{
if (items == null)
{
return;
}
foreach (T item in items)
{
Add(item);
}
}
public HashSet<T> NullSet { get { return new HashSet<T>(); } }
#region ICollection<T> Members
public void Add(T item)
{
if (null == item)
{
throw new ArgumentNullException("item");
}
dict[item] = null;
}
public void Clear()
{
dict.Clear();
}
public bool Contains(T item)
{
return dict.ContainsKey(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
if (array == null) throw new ArgumentNullException("array");
if (arrayIndex < 0 || arrayIndex >= array.Length || arrayIndex >= Count)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
dict.Keys.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
return dict.Remove(item);
}
public int Count
{
get { return dict.Count; }
}
public bool IsReadOnly
{
get
{
return false;
}
}
#endregion
public HashSet<T> Union(HashSet<T> set)
{
HashSet<T> unionSet = new HashSet<T>(this);
if (null == set)
{
return unionSet;
}
foreach (T item in set)
{
if (unionSet.Contains(item))
{
continue;
}
unionSet.Add(item);
}
return unionSet;
}
public HashSet<T> Subtract(HashSet<T> set)
{
HashSet<T> subtractSet = new HashSet<T>(this);
if (null == set)
{
return subtractSet;
}
foreach (T item in set)
{
if (!subtractSet.Contains(item))
{
continue;
}
subtractSet.dict.Remove(item);
}
return subtractSet;
}
public bool IsSubsetOf(HashSet<T> set)
{
HashSet<T> setToCompare = set ?? NullSet;
foreach (T item in this)
{
if (!setToCompare.Contains(item))
{
return false;
}
}
return true;
}
public HashSet<T> Intersection(HashSet<T> set)
{
HashSet<T> intersectionSet = NullSet;
if (null == set)
{
return intersectionSet;
}
foreach (T item in this)
{
if (!set.Contains(item))
{
continue;
}
intersectionSet.Add(item);
}
foreach (T item in set)
{
if (!Contains(item) || intersectionSet.Contains(item))
{
continue;
}
intersectionSet.Add(item);
}
return intersectionSet;
}
public bool IsProperSubsetOf(HashSet<T> set)
{
HashSet<T> setToCompare = set ?? NullSet;
// A is a proper subset of a if the b is a subset of a and a != b
return (IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this));
}
public bool IsSupersetOf(HashSet<T> set)
{
HashSet<T> setToCompare = set ?? NullSet;
foreach (T item in setToCompare)
{
if (!Contains(item))
{
return false;
}
}
return true;
}
public bool IsProperSupersetOf(HashSet<T> set)
{
HashSet<T> setToCompare = set ?? NullSet;
// B is a proper superset of a if b is a superset of a and a != b
return (IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this));
}
public List<T> ToList()
{
return new List<T>(this);
}
#region Implementation of ISerializable
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null) throw new ArgumentNullException("info");
dict.GetObjectData(info, context);
}
#endregion
#region Implementation of IDeserializationCallback
public void OnDeserialization(object sender)
{
dict.OnDeserialization(sender);
}
#endregion
#region Implementation of IEnumerable
public IEnumerator<T> GetEnumerator()
{
return dict.Keys.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
CopyURL 0.1.3 released
September 3rd, 2008
At a user’s request, I’ve added a context menu item for CopyURL, which can be enabled/disabled in the options page. Just another option if you don’t want to use the toolbar menu button.
It’s in the usual place: CopyURL (might take awhile for the page cache to refresh, but it’s definitely there).
TextSaver addon tutorial
July 4th, 2008
Although I’ve already entered CopyURL and Affiliator in the Extend Firefox 3 competition, I wanted to see what I could do with 3 days of intense coding. My wife always complains about writing long posts on YouTube, then losing them due to a session timeout. I’m always looking for a problem to fix, and this seemed like a perfect idea for the competition, so starting this past Monday, I created TextSaver.
When you load the addon and restart Firefox, you should see a little disk-and-paper icon in the statusbar. We’ll get to that in a minute. To start, go ahead and type something in a text input, whether it be a standard textbox, a textarea, or whatever magic Google uses in GMail (which do not appear to be standard textboxes according to Firebug). Click “Add this text to TextSaver”, and you should see a sidebar appear, along with a box prompting you for a name. Pick whatever name you’d like to remember this snippet of text by, and click “OK”. The text is now safely stored in the sidebar, ready to be dragged and dropped on any text input, even those outside of Firefox.
You can also drag and drop pretty much anything that has a text representation onto the sidebar, and the process will remain the same. Clicking the globe icon next to the item’s name will open a tab containing the page you were on when you saved the text. Clicking “(edit)” will pop up an edit dialog allowing you to modify the name, URL, or content of the saved text. Finally, clicking the close button in an item will allow you to delete it permanently.
You may have also noticed the search box above the saved text items. As you type in this box, it will rapidly search through the names, URLs, and contents of every piece of text you’ve saved with TextSaver, filtering out those that don’t match. This is a quick way, for example, of finding that post on YouTube out of the hundreds that have timed out. Hopefully you’ll have deleted some after you’re done using them, but if you’ve got the desire to keep them around, feel free to do so.
Clicking on the TextSaver icon in the status bar will pop up a menu giving you the option to show/hide the sidebar, as well as whether or not to show the context menu item for TextSaver. If you prefer dragging/dropping, and don’t want to lose valuable context menu real estate, get rid of it, and it’ll never bother you again.
Thanks for trying TextSaver. I welcome all feedback, critiques, and complaints. If you’ve found it helpful, donations are also gladly accepted, and greatly appreciated.
Switching hosts
June 27th, 2008
Well, Dreamhost has informed me that my hosting costs for the next year are going to triple, so it’s time to find another host. As soon as I figure out who that host will be, I’ll move the blog and update the extensions on Mozilla’s site to point to the new homepage, or possibly even a dynamic redirect from DynDNS so I don’t have to do this again next year.
CopyURL 0.1 addon for Firefox released
June 24th, 2008
An Affiliator user (who had just moved to Firefox 3 from Safari) requested some help with copying links formatted in HTML, which Affiliator does automatically, though only for Amazon, and which Safari apparently does natively. Not finding anything, I decided to rip out that portion of Affiliator, spruce it up a bit, and throw it in the toolbar.
CopyURL was the result.
Load up the addon in Firefox, then either go to View->Toolbars->Customize…, or right-click the toolbar and choose “Customize…”. Drag and drop the CopyURL icon anywhere you like on the toolbar, and you’re set. Click the button on any page or image on the web, and it will do one of two things:
- If you’re on a webpage, it will create an anchor tag to the current page, using the page’s TITLE element text as the link text. If the TITLE element can’t be found, it will prompt you to enter the link text.
- If you’re on an image, it will create an image tag.
No more having to remember HTML syntax when you want to post a link, or BBCode or Wiki syntax, either. Clicking the drop-down arrow next to the button will give you a list of formats to choose from, as well as the ability to add your own.
Hopefully this will make things a little easier for everyone.
Also, a new (minor) version of Affiliator is probably coming tomorrow, as the same user who prompted CopyURL informed me that right-click context menus are particularly difficult with one-button mice on OSX. In response to that, Affiliator’s getting a toolbar button as well, though it won’t be as full-featured as the context menu.
Lastly, I’m aware the icon sucks. Despite minoring in Art & Design in college, I can’t make icons for crap.
Affiliator 0.1.3 released
May 28th, 2008
I apologize for the lack of updates recently. My real job has kept me very busy for the past few months. This is just a quick update to allow you to change the format of the generated HTML links, as requested by a user. The user who requested this needed his links in the following format:
[http://www.amazon.com/dp/B00000JRSB/?tag=doggettc-20][Buy Final Fantasy VII] (seriously, buy it from the third-party seller with the same name as me, so I can build a new non-slow computer)
The simplest solution was to allow you to specify your own link formats, so I’ve added a field to the Affiliator options page under Tools->Addons that allows just that. To help you get started, here are some common formats:
- Default HTML: <a href=”#{GENERATED_LINK}” target=”_blank”>#{PRODUCT_TITLE}</a>
- BBCode: [url=#{GENERATED_LINK}]#{PRODUCT_TITLE}[/url]
- User request from above: [#{GENERATED_LINK}][Buy #{PRODUCT_TITLE}]
It should be fairly easy to get your links into whatever format you (or your blog/site) prefer. Just make sure you set your generated links to auto-wrap after setting your format, and the work will be done for you.
I’ve also added a PayPal donation link to the sidebar, at the same user’s suggestion. If you’d like to support the development of Affiliator, all donations are greatly appreciated. They will mostly be used to buy turkey for the cat pictured in the sidebar, to keep him from crying and distracting me from writing code.
Lastly, I haven’t forgotten the users’ requests for multiple international affiliate link generation (with link status checking). That’s coming soon, I just haven’t had time to work on this lately, and I’m horribly sorry for the delay.
I lied, that wasn’t the last thing. Here’s the link for the new version on Mozilla Addons:
If you like Affiliator, please write a review on that page. The more reviews it gets, the sooner it goes public, which means it will automatically notify you of updates.
Thank you all again for your support of this project.
Half-assed Functional Programming in .NET 2.0
April 8th, 2008
Ever since reading Joel’s Can Your Programming Language Do This?, I’ve been obsessed with Functional Programming, and with my shiny new hammer, everything looks like a nail. A few months ago, I got it into my head that it’d be a good idea to implement Map, Filter, and Reduce in .NET 1.1. It was actually simple enough to do, albeit in no way type-safe, and still vulnerable to side effects. Naturally, I named the library QAFP (Quarter-Assed Functional Programming), since it was half-half-assed. As an example, here was my Map function:
public delegate object MapFunction(object obj);
public static IList Map(IList list, MapFunction func)
{
if (null == list || null == func)
{
return null;
}
IList mappedList = new ArrayList(list.Count);
foreach (object o in list)
{
mappedList.Add(func(o));
}
return mappedList;
}
public object Square(object obj)
{
int i = (int) obj;
return i * i;
}
public void MapTest()
{
IList numbers = new ArrayList(5);
for(int i = 1; i <= 5; i++)
{
numbers.Add(i);
}
numbers = Map(numbers, Square);
// list should now contain [1,4,9,16,25]
}
It was ugly, but it worked. Pass it a list of mixed items and forget to do your type checking, and you’re boned. It wound up being very useful, and cut down on some of our development time, until we finally moved up to .NET 2.0. I know all of this stuff is old news to those of you using .NET 3.5 and LINQ, but given that we’ve just updated to 2.0, 3.5 is a long way off for us, so bear with me. If you’re stuck using .NET 2.0, you may enjoy this.
The upgrade to half-assed functional programming came with generics and anonymous delegates. Since side-effects are still possible, it’s not pure functional programming, but it’s close enough to be very useful, as long as you’re careful with it. The new code doesn’t look all that different, but it’s much safer, and any type errors should be caught by the compiler. Here’s the new Map function:
public delegate TOutput MapFunction<tinput, TOutput>(TInput obj);
public static void Map<tinput, TOutput>(List<tinput> inputList, MapFunction<tinput, TOutput> func, out List<toutput> outputList)
{
outputList = new List<toutput>();
if (null == inputList || null == func)
{
return;
}
outputList.Capacity = inputList.Count;
foreach (TInput obj in inputList)
{
outputList.Add(func(obj));
}
}
public int Square(int i)
{
return i * i;
}
public void MapTest()
{
List<int> numbers = new List<int>(5);
List<int> squaredNumbers;
for(int i = 1; i <= 5; i++)
{
numbers.Add(i);
}
Map(numbers, Square, out squaredNumbers);
// list should now contain [1,4,9,16,25]
}
Filtering a list is also needed quite a bit, and the generic List<T> in .NET 2.0 already has this covered, through the use of Predicates. A Predicate is basically a function that takes an object of type T, and returns true or false if it matches some criteria. Combine that with List<T>’s FindAll function, and it’s easy to return a list of Ts matching that criteria. I still wrapped everything in a filter function, just to ensure no empty lists or missing predicates. As an example, to strip out all odd numbers from a list:
public static List<tinput> Filter<tinput>(List<tinput> inputList, Predicate<tinput> func)
{
if (null == inputList || null == func)
{
return null;
}
return inputList.FindAll(func);
}
public List<int> GetEvenNumbers(List<int> numbers)
{
return Filter(numbers, delegate(int i){ return (i % 2) == 1; });
}
Last, but certainly not least, is Reduce, also known as foldr to those of you who speak with a LISP. For those not in the know, it performs an operation on each item in the list and the total (or aggregate) of every item before it, starting with an initial value for the total. The way this total is implemented is completely up to you, as you can see below:
public delegate TAggregate ReduceFunction<tinput, TAggregate>(TAggregate aggregate, TInput obj);
public static TAggregate Reduce<tinput, TAggregate>(List<tinput> inputList, ReduceFunction<tinput, TAggregate> func, TAggregate initialValue)
{
TAggregate aggregate = initialValue;
inputList.ForEach(delegate(TInput input) {
aggregate = func(aggregate, input);
});
return aggregate;
}
public int Sum(int total, int i)
{
return total + i;
}
public int Product(int total, int i)
{
return total * i;
}
public StringBuilder Concatenate(StringBuilder sb, string s)
{
return sb.AppendFormat("{0}", s);
}
public void TestReduce()
{
List<int> numbers = new List<int>(5);
for(int i = 1; i <= 5; i++)
{
numbers.Add(i);
}
int sum = Reduce(numbers, Sum, 0);
int product = Reduce(numbers, Product, 1);
List<string> strings = GetListOfStringsToConcatenate();
StringBuilder sb = new StringBuilder();
sb = Reduce(strings, Concatenate, sb);
}
Now, where and why would you use this? Functional Programming is usually used for math-intensive applications, but like I said, everything looks like a nail. To improve network performance, when making remote function calls, we don’t send entire objects if we only need their ID number. So, given a list of patients, let’s say we want to find those with fatal allergies, to keep them from falling into our strategically placed peanut, grass, and pet dander bins (let’s also say we’re witch doctors).
public List<int> GetAllergiesForPatientsAboveSeverity(List<int> patientIDs, Severity severity)
{
List<patient> patients;
List<int> allergyIDs;
Map(patientIDs, GetPatientObjectFromID, out patients);
List<allergy> allergies =
GetAllAllergiesForPatients(patients)
.Filter(delegate(Allergy allergy) {
return allergy.Severity >= severity;
});
Map(allergies, GetAllergyIDFromObject, out allergyIDs);
return allergyIDs;
}
public List<allergy> GetAllAllergiesForPatients(List<patient> patients)
{
// Grab the allergies for the given patients…
}
public Patient GetPatientObjectFromID(int patientID)
{
return PatientCache.Find(delegate(Patient p) { return p.ID == patientID; };
}
public int GetAllergyIDFromObject(Allergy allergy)
{
return allergy.ID;
}
We’d end up only sending a few ints to the server, and getting a few ints in return. Why is this important? Hell if I know, I just think it’s elegant code, if a contrived example. So, if you’re stuck with .NET 2.0 for the moment, like I am, and you’re into Functional Programming, give this a shot. I’m sure you’ll find all sorts of nails you never noticed before.
Affiliator 0.1.2 released
April 8th, 2008
This is just a maxVersion bump to allow Affiliator to work with the Firefox nightly builds. There are no new features, so if you’re happy with 0.1.1, stick with it.
I won’t bother to host it here since it’s such a small change, so you can grab it at the Mozilla Addons Directory here: Affiliator 0.1.2.
Affiliator 0.1.1 released
April 6th, 2008
Ann Zeise over at the Amazon Associates discussion boards pointed out to me that the link format I was using was outdated, and the preferred format is:
http://www.amazon.com/dp/ASIN/?tag=YourID
I’ve updated the addon to not only reflect the proper link format, but also to add it as a preference, so if Amazon changes the preferred format, you can update the string yourself until I get a fix out. Currently, it defaults to:
http://www.amazon.com/dp/#{ASIN}/?tag=#{AFFILIATE_CODE}
All you have to do is update the URL, and put the #{ASIN} and #{AFFILIATE_CODE} markers in the proper place, and it’ll work like it should.
I also added the ability to click on any product link on Amazon and have it generate the affiliate link. You no longer have to be on the actual product page. From what I’ve found, Amazon provides three link formats on their pages:
- http://www.amazon.com/gp/product/#{ASIN}/blah
- http://www.amazon.com/#{PRODUCT_TITLE}/dp/#{ASIN}/blah
- http://www.amazon.com/dp/#{ASIN}/blah
If the link clicked doesn’t contain the product title, it will attempt to get it from the link text, unless the link is an image, in which case you will be prompted for the product name. If it does contain the product title, it will use that for the product name. All of these cases, of course, depend on you having set Affiliator to automatically wrap your link in an HTML tag. If you’re just generating the link, it skips this whole process.
I’m going to continue to use this blog for Affiliator update notifications, and you can always find the latest news here:
http://www.theantichris.dreamhosters.com/category/firefox-addons/affiliator/
I’ve also submitted Affiliator to the Mozilla Addons Directory, but it won’t be public until a few people have submitted reviews, so if you like it, head over there and submit a review. The benefit to that is, whenever Affiliator is updated, Firefox will notify you automatically.
Until then, you can get Affiliator 0.1.1 here:
Affiliator 0.1.1
Mozilla Addons Directory page for Affiliator
Affiliator addon released for Firefox
April 3rd, 2008
I’ve always found it a bit of a pain to generate Amazon affiliate links. Once you’ve found the page you want to link to, you have to go to the affiliate site, log in, pick what type of link you want, enter the address and what you want the link to say. Then, after all that, you get a page with your link and a tracker image. The whole process usually takes me about a minute per link, sometimes less depending on how the network’s behaving.
Like any decent 20-percenter, I’ll gladly spend a day building a tool to save myself minutes. It’ll pay itself off in a few thousand links. Anyway, the current method of generating affiliate links has two problems:
- The generated link is long and cumbersome, filled with GET parameters.
- It’s slow.
The first problem has been solved by a variety of people, but I first saw it on Coding Horror. You can greatly shorten the URLs, from this:
http://www.amazon.com/gp/redirect.html?ie=UTF8&location=http%3A%2F
2Fwww.amazon.com %2FSamsung-SyncMaster-226BW-LCD-Monitor
%2Fdp%2FB000NBBWNU%3Fie%3DUTF8%26s%3Delectronics%26qid%3D1207233586%26sr
%3D8-1&tag=doggettc-20&linkCode=ur2 &camp=1789&creative=9325
to this:
http://www.amazon.com/exec/obidos/ASIN/B000NBBWNU/doggettc-20
All you need is the ASIN (Amazon Standard Item Number) and your affiliate code, which make up the last two segments of the URL.
First problem solved, but you still have to hunt through the page for the ASIN. I hunted around, and found that nobody had made a Firefox addon to build affiliate links, so I took on the task myself. The result is Affiliator, a one-click solution. OK, three clicks.
All you need to do now is find the product you want to link to, right-click the page, and:
The affiliate link is generated and copied to the clipboard. If you’re super lazy, like myself, you can choose to have your links automatically wrapped in an HTML anchor tag with the title of the item on the page. With three quick clicks, the item I showed above becomes this:
Samsung SyncMaster 226BW 22″ LCD Monitor
(A great monitor that has unfortunately gone back up in price since I bought it a year ago.)
Hopefully someone will find it useful. If not, it’ll be worth my time after 1,439 more links.
In case you missed the link above, you can download the addon here:


