gists, csharp, net comments edit

While .NET provides the sorting of collections through things like Array.Sort and the IComparer interface, there’s no real generic ability to filter elements out of a collection based on arbitrary criteria.

dasBlog implements collection-specific filtering in the various strongly-typed collections by adding static methods to the collections that allow you to pass in a collection and a filter criteria delegate and have a new, filtered version of the collection returned to you.

I thought it might be handy to have a more generic version of that ability so you could filter any collection implementing the IList interface. It would allow you to have a single way to filter lists of any type - all you’d have to do is cast the resulting collection back to the type you originally passed in.

Here’s what I came up with:

using System;
using System.Collections;

namespace Paraesthesia.Collections {

  public delegate bool ListFilterCriteria(object obj);

  public sealed class ListFilter {

    private ListFilter(){}

    public static IList Filter(IList toFilter, ListFilterCriteria criteria){
      // Check parameters
      if(toFilter == null){
        throw new ArgumentNullException("toFilter", "The IList to filter must not be null.");
      }
      if(criteria == null){
        throw new ArgumentNullException("criteria", "The collection filter criteria must not be null.");
      }

      // Get the invocation list
      System.Delegate[] invocationList = criteria.GetInvocationList();
      if(invocationList.Length < 1){
        throw new ArgumentException("There must be at least one delegate in the invocation list of the filter criteria.", "criteria");
      }

      // Create the output collection
      IList filtered = null;
      try{
        // Get the input collection type
        Type inputType = toFilter.GetType();

        // Create the new object
        filtered = Activator.CreateInstance(inputType) as IList;
      }
      catch(Exception err){
        throw new NotSupportedException("Error occurred while creating new collection to contain filtered list.", err);
      }
      if(filtered == null){
        throw new NotSupportedException("Unable to create new collection to contain filtered list (constructor invocation returned null).");
      }

      // Perform the filtering
      foreach(object obj in toFilter){
        bool include = true;
        foreach(ListFilterCriteria individualCriteria in invocationList){
          include = include && individualCriteria(obj);
          if(!include){
            break;
          }
        }
        if(include){
          filtered.Add(obj);
        }
      }

      // Filtering complete; return the filtered collection
      return filtered;
    }
  }
}

The idea is that you create a method that takes in an object and returns a Boolean indicating if it should be included in the filtered collection or not. Then pass your collection through the filter with the criteria specified and a filtered version of the collection gets returned to you - cast it back to the appropriate type and continue on your merry way.

Your filter criteria might look like this:

using System;

namespace MyNamespace{
  public class MyCriteriaClass{
    public static bool FilterThreeChars(object obj){
      String toCheck = obj as String;
      if(toCheck == null){
        return false;
      }
      return toCheck.Length == 3;
    }
  }
}

Then your use of the filter might look like this:

using System;
using System.Collections.Specialized;
using Paraesthesia.Collections;

namespace MyNamespace{
  public class MyTestClass{
    public void TestTheFilter(){
      // Create the original collection
      StringCollection coll = new StringCollection();
      coll.Add("a");
      coll.Add("bc");
      coll.Add("def");
      coll.Add("ghij");
      coll.Add("klmno");

      // Set up the filter criteria delegate
      ListFilterCriteria criteria =
        new ListFilterCriteria(MyCriteriaClass.FilterThreeChars);

      // Filter the collection
      StringCollection filtered = ListFilter.Filter(coll, criteria) as StringCollection;

      // The filtered collection only contains "def"
    }
  }
}

I did some performance testing on this versus a similarly structured filtering service that is strongly-typed and the two were comparable. Your mileage may vary.

Note: Code is provided free, but also without support. If it breaks, doesn’t work, isn’t optimized to your liking, etc., feel free to fix it, but I’m not going to actively answer questions on it or help you figure out why it’s not working for you.

General Ramblings comments edit

Saturday was spent primarily trucking around from fabric store to fabric store looking for the right fabric for the shirt for my Willy Wonka costume. I discovered that generally the price of fabric is directly proportional to the size of the store. For example, at the Fabric Depot they have a huge selection, but a particular fabric was $10/yard there. At a small Jo-Ann, the same thing can be had for $2.50/yard. Now, granted, you’re paying for selection and convenience at Fabric Depot, but that’s a little spendy, thank-you-very-much. I bought at Jo-Ann.

Sunday was a family reunion for the people on my mom’s side of the family. It took place at Rainbow Falls State Park in Washington. Nice park, not a whole lot of people, so it was a good spot.

The thing about a “family reunion” for that side of the family is that I see these people all the time. All the time. Holidays, birthdays, random times of the year… Maybe not all simultaneously (that’s almost more than a person can handle, if you know what I mean), but I see them all the time. So it’s not really a “reunion” so much as “another get-together.”

I hadn’t eaten all day, and the agenda for the event said we were supposed to get there and eat at noon. After a nice couple-hour trek into the sticks, we got there and when noon rolled around… for some reason, we weren’t eating. I’m really not sure why. I think we might have been waiting for someone or something, but… well, here’s my take (and if/when I run some event on my own, this is how it’s gonna be): The schedule says noon. Come hell or high water, if you aren’t there at noon, you just get to eat late. We’re eating at damn well noon. I’m not waiting for you.

One o’clock rolls around, still waiting. The hunger has passed beyond me to other people and the finger foods started disappearing - anything you could walk past and grab without a lot of people noticing. Probably 1:30p hit and we started eating. Actually, it was more like, “we started milling around closer to the food with the intent to eat.” Here’s another one: I have no issues being “the first to eat.” I don’t care if you’re the first either. The person closest to the head of where the line will eventually be had better get his/her ass in gear and get going. If we’re not eating on time, I no longer have the patience to wait and see who’s digging in first. We’re all eating the same food here

  • let’s get on with it.

I’m sure I ate more than my fill, but there’s always a lot of food left, so even being fuller than full (as I’m sure everyone else was, too), everyone ended up taking something home.

After the feeding died down, we were all sitting around talking, which is probably the most entertaining part of any of these get-togethers since it seems random crap happens to that side of the family a lot. I’m not sure if we bring it on ourselves or if it’s just luck. For example:

My aunt and her boyfriend were at the Clackamas Les Schwab getting tires or something the other day. Apparently there was nowhere to sit inside, so they went outside. Not finding any benches, my aunt’s boyfriend went over to the brick-wide ledge surrounding the front window of the place and perched up on that. My aunt followed suit, wedging her ass up on the ledge until -

Crash!

The front window of the Les Schwab broke. I guess it cracked from top to bottom and side to side. She claims there were bullet holes in the window that had weakened it but… well, keep telling yourself that. Hehehehe.

They went back inside and were greeted at the front desk as “Mr. and Mrs. Crash.” Rock on.

I guess if you go to that Les Schwab the window is taped up with duct tape while they wait for a replacement. (My aunt didn’t have to pay; Les Schwab’s covering that. Maybe they’ll put a bench outside, too?)

A couple hour drive home and we were finally back by around 8:00p. It was a long day, and I think I’m still sort of recovering from it. (I’m not really a car-trip person, particularly crammed into the back seat.)

General Ramblings comments edit

I’ve always enjoyed the concept of highly detailed and accurate costumes. This crap they sell on the market for $15 that includes the shirt, hat, boot covers, and a laser gun all in one package might fly for less detail-oriented folks or people short on time, but I’m a big fan of authenticity.

The problem is, I generally end up being one of the “short on time” crowd, so I half-ass myself a costume at the last minute involving a quick trip to the military surplus store and a liberal application of creativity involving everyday household items. Halloween shows up altogether too quickly and the night before I’m cobbling something together. It kills me, too - I’d like to have something better than that. If I’m going as Dracula, I want a pair of custom fangs and a high-quality cape. I have yet to be a Ghostbuster because I need accurate props with real working electronics.

This year I’m taking the project on early, though: I’m going to be Willy Wonka (from the new movie).

Jenn and I went last night to the fabric store and I ended up picking up three patterns - one for a coat (which will need to have the collar slightly modified); one for a shirt (I’ll have to find some paisley fabric); and one for a civil war jacket which, once the sleeves and collar are removed, looks just like his vest. I’ve also purchased some finials that can be used as the handle on his walking stick and I’ve ordered some clear PVC pipe for the body of it.

I’m getting a head start so I can actually make all this stuff and go for the authentic look. Now, admittedly, I’m already conceding a few of the details. I won’t be able to get the “W” pin he has at his collar. I’m not a good enough tailor to make a coat that has pinstripes that match up so I’m going to go with a solid color instead. The shoes probably won’t be quite right, and the bottom of the pants (if I go with a pair I have already) won’t be right, either. And I can’t find the actual finial that was on his cane, nor can I find quite the right tip for it, either, but we’ll make due. Oh, and there’s no way I’m springing for a real top hat. The point is, it’ll be of a much higher quality (and will be much more fun) than the $15 kit.

Of course, the patterns already cost me $25, the finials were $15, and the PVC pipe was $30 with shipping (it only comes in 10’ sections). So budget-wise, I’m way over the typical Halloween costume budget already. It’ll be fun to make, though, and I think that’s really what matters. Plus I’ll be happier with it when it’s done. I’ll keep you posted.

UPDATE 10/02/05: The costume came out well. Here are the details.

General Ramblings comments edit

ScoobaIt appears that the latest from iRobot - the Scooba, a floor-mopping robot - will be available for purchase online in November and should be in retail stores in early 2006. I’m actually pretty stoked. I have a Roomba (their vacuuming robot) and feel like it was definitely $200 well spent.

For $399, you’ll get the Scooba, some cleaning solution, a measuring cup, a battery, a battery charger, a virtual wall, and what appears to be an AC adapter of some nature (probably to charge the Scooba while the battery’s still in it).

The virtual walls used by Scooba look different than the ones used by Roomba - they look more like the new scheduling walls, actually. I’m curious if the virtual walls with Scooba are compatible with the Roomba walls or if I’m going to have to buy a bunch of Scooba walls, too.

Scooba doesn’t come with a “home base” the way Roomba does (or, at least, some Roomba models), and the batteries look different. One of the things I like about Roomba is that when it’s done, it goes home to charge up. I can’t tell if Scooba has that ability or not. It almost certainly doesn’t out of the box, as it doesn’t come with the requisite charging station, only manual chargers.

None of that really deters me, though - it’s the $400 price tag that’s got me. $200 was the perfect price for Roomba (admittedly, that was with a 20% off coupon at Bed Bath and Beyond). I’m just not sure about Scooba for double the price. I’d have to see it in action in person, or at least hear from someone who has one.

I’ll have to think about it. I do have some 20% off coupons burning a hole in my pocket, and $320 is a lot better than $400…