Using SortedSet as a Sorted List with Custom Comparer – c#

Sometimes you have a list of objects you want to stay sorted. You can usually use an IList and then linq: orderby to sort the list – but other times it helps to have the list always sorted. In those times, SortedSet works very well. In my below example, a sorted set contains some people – and whenever you look at the list in the SortedSet – they are already sorted (by age, name & location).

I was concerned about the performance of the SortedSet vs. a List, so after running some tests, as expected – the SortedSet needs some initial time when adding items (to compare) vs. the List which will is fast to write to – but needs a little time when sorting the list after. Below is some really fast numbers (data in milliseconds) I’ve compiled based on adding / reading 25000 records:

sortedSet-performance

Below is the sample using SortedSet and an IComparer:

using System;
using System.Collections.Generic;

namespace sortedSet
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            //create the SortedSet
            SortedSet<Person> people = new SortedSet<Person>(new PersonComparer());

            //add some random folks:
            Random rnd = new Random();
            for (int i = 1; i <= 25; i++)
            {
                //new person
                Person person = new Person();
                person.name = "Bob " + i.ToString();
                person.location = "Miami" + i.ToString();

                //random age
                int r = rnd.Next(1, 99);
                person.age = r;

                //add person to set
                people.Add(person);
            }

            //show our list:
            foreach (Person person in people)
            {
                Console.WriteLine(person.name + " - Age: " + person.age.ToString());
            }

            Console.ReadLine();
        }
    }

    //create comparer
    internal class PersonComparer : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            //first by age
            int result = x.age.CompareTo(y.age);

            //then name
            if (result == 0)
                result = x.name.CompareTo(y.name);

            //a third sort
            if (result == 0)
                result = x.location.CompareTo(y.location);

            return result;
        }
    }

    internal class Person
    {
        public string name { get; set; }

        public string location { get; set; }

        public int age { get; set; }
    }
}

-If you don’t have a need to sort your lists – don’t forget about HashSet (very fast).

Using SortedSet as a Sorted List with Custom Comparer – c#

MongoDB – Linking Records / Documents Using MongoDBRef

If you’re using MongoDB, you know as a document database it doesn’t provide the “join” feature you’ve come to rely on in standard relation databases (sql server, mySql, postgres). In many instances, you’re not using Mongo as a replacement for your existing data structure / methods – but it would be nice to relate some documents (without embedding everything [potentially duplicating data] into a single document.

I am a big fan of MongoDB and the .net / c# driver. Below is a method I’ve found that works well for relating different documents using MongoDBRef. In my scenario – which I’m sure many have better methods – I’m using a property to keep a list of related documents – then a method to retrieve the related documents if needed.

I’m my below example, I’m creating trains, train cars, and passengers – then relating them. In short: I’m using IList<MongoDBRef> to store the list, and FetchDBRefAs to get the documents again.

This works really well and is flexible. I’ve tested this against many records and have found that if you’re looking to get thousands of records with their related thousands of records – you won’t experience the same performance as a standard relational db join. But for a few records (hundreds in my testing) it’s fast and efficient. I’m also using .AsParallel() on the query – this gained me about 10%+ performance.

One note: if you’re allowing Mongo to generated the document id on insertion, queuing up records in a bulk operation (ie: InitializeOrderedBulkOperation) you’ll soon realize there isn’t an id yet to use for the MongoDBRef.

Comment if you’ve found a better method you prefer for linking documents.

My example:

using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace mongoDBlinked
{
    public class passenger
    {
        public ObjectId Id { get; set; }

        public string Name { get; set; }
    }

    public class trainCar
    {
        public ObjectId Id { get; set; }

        public string carNo { get; set; }

        public string serial { get; set; }

        public string note { get; set; }

        public IList<MongoDBRef> passengers { get; set; }

        public IList<passenger> GetPassengers(MongoDatabase db)
        {
            if (passengers.Count == 0)
                return new List<passenger>();

            IList<passenger> tpass = new List<passenger>();
            foreach (var related in passengers)
            {
                tpass.Add(db.FetchDBRefAs<passenger>(related));
            }

            return tpass;
        }
    }

    public class train
    {
        public ObjectId Id { get; set; }

        public string trainNo { get; set; }

        public string note { get; set; }

        public IList<MongoDBRef> trainCars { get; set; }

        public IList<trainCar> relTrainCards { get; set; }

        public IList<trainCar> GetTraincars(MongoDatabase db)
        {
            if (trainCars.Count == 0)
            {
                return new List<trainCar>();
            }

            IList<trainCar> tcars = new List<trainCar>();
            foreach (var related in trainCars)
            {
                tcars.Add(db.FetchDBRefAs<trainCar>(related));
            }

            return tcars;
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            
            var server = new MongoClient("mongodb://localhost").GetServer();
            var database = server.GetDatabase("testmongodb");

            var collection = database.GetCollection<trainCar>("trainCars");
            var trainCollection = database.GetCollection<train>("trains");
            var peopleCollection = database.GetCollection<passenger>("passengers");
            

            //add some data
            for (int t = 0; t < 20; t++)
            {
                train newTrain = new train();
                newTrain.note = "nyc" + t.ToString();
                newTrain.trainNo = "345";
                newTrain.trainCars = new List<MongoDBRef>();

               

                //add some cars:

                for (int i = 0; i < 50; i++)
                {
                    trainCar tcar = new trainCar();

                    tcar.carNo = "0" + i.ToString();
                    tcar.note = "Needs new brakes";
                    tcar.serial = "1234";
                    tcar.passengers = new List<MongoDBRef>();
                    for (int p = 0; p < 3; p++)
                    {
                        passenger pass = new passenger();
                        pass.Name = "name" + p.ToString();
                        peopleCollection.Insert(pass);
                        tcar.passengers.Add(new MongoDBRef(peopleCollection.Name, pass.Id));
                    }

                    collection.Insert(tcar);
                 
                    newTrain.trainCars.Add(new MongoDBRef(collection.Name, tcar.Id));
                }

                

                trainCollection.Insert(newTrain);
            }
            Console.WriteLine("saved");
           

            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            //get the data
            //asparallel gained me about 15% of qry time
            var query = from v in trainCollection.AsQueryable<train>().AsParallel()
                        select v;

            foreach (train aTrain in query)
            {
                Console.WriteLine("train: " + aTrain.note);
                foreach (trainCar tcar2 in aTrain.GetTraincars(database))
                {
                    if (tcar2 != null)
                    {
                        Console.WriteLine("car: " + tcar2.carNo);
                        foreach (passenger pass in tcar2.GetPassengers(database))
                        {
                            Console.WriteLine("car: " + tcar2.carNo + " pass: " + pass.Name);
                        }
                    }
                }

               
            }
            Console.WriteLine("trains loaded");

            stopWatch.Stop();           
            TimeSpan ts = stopWatch.Elapsed;

            
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                ts.Hours, ts.Minutes, ts.Seconds,
                ts.Milliseconds / 10);
            Console.WriteLine("RunTime " + elapsedTime);

            

            Console.ReadLine();
        }
    }
}
MongoDB – Linking Records / Documents Using MongoDBRef

Force WWW & Fix Redundant Hostnames on Google / SEO

I feel the term “SEO” is completely overused, however, there are a few things you want to do besides just having great content. One is make sure your site url is consistent. chrisbitting.com is different from http://www.chrisbitting.com. 

Google Analytics will provide you a suggestion to fix this if you’re experiencing traffic from multiple hostnames. Something like:

Property http://www.yourdomain.com is receiving data from redundant hostnames. Some of the redundant hostnames are:

This is easy to fix using your Global.asax page. Just add this to your code, replacing “yourdomain” with your actual domain. The Application_BeginRequest will catch and redirect to the altered url, also issuing a 301 to help search engines.

void Application_BeginRequest(object sender, EventArgs e)
    {
        

        if (HttpContext.Current.Request.Url.ToString().ToLower().Contains(

            "http://yourdomain.com"))
        {

            HttpContext.Current.Response.Status = "301 Moved Permanently";

            HttpContext.Current.Response.AddHeader("Location",

                HttpContext.Current.Request.Url.AbsoluteUri.ToString().ToLower().Replace(

                    "http://yourdomain.com", "http://www.yourdomain.com"));

            HttpContext.Current.Response.End();
        }
    }

You could do this using web.config + rewrite, but I enjoy this method more.

Force WWW & Fix Redundant Hostnames on Google / SEO

Error 500.19 with IIS / rewrite in web.config

500-19If you’ve added a rewrite to you web.config but are now receiving a HTTP Error 500.19 Internal Server Error, chances are you need to install the URL Rewrite Module. You might have assumed this is part of your install but it might be missing.

The install only takes a few seconds.

 

  1. Visit http://www.iis.net/downloads/microsoft/url-rewrite
  2. At the bottom of the page you can download the installer without using Web Platform.
  3. Run the MSI installer to add the URL Rewrite Module to your server. In my experience, I have not needed to reboot or reset IIS after installing this.
  4. Now you should successfully be able to use <rewrite>.

rewrite-mod

Error 500.19 with IIS / rewrite in web.config

C# – Measure String Width & Height (in pixels) Including Multi-line Text

A while back I posted a VB snippet of a simple way to measure text length in pixels. The below is a C# version, with some minor improvements (like not needing a form, so it’s okay to use with console and asp.net apps).

To measure a single line string:

//set font, size & style
Font f = new Font("Microsoft Sans Serif", 14, FontStyle.Regular);

//create a bmp / graphic to use MeasureString on
Bitmap b = new Bitmap(2200, 2200);
Graphics g = Graphics.FromImage(b);

//measure the string
SizeF sizeOfString = new SizeF();
sizeOfString = g.MeasureString("This is a text line", f);

//use:
Debug.WriteLine("String Height: " + sizeOfString.Height);
Debug.WriteLine("String Width: " + sizeOfString.Width);

This is a multi-line string – notice the addition of the width parameter:

//set font, size & style
Font f = new Font("Microsoft Sans Serif", 14, FontStyle.Regular);

//create a bmp / graphic to use MeasureString on
Bitmap b = new Bitmap(2200, 2200);
Graphics g = Graphics.FromImage(b);

//measure the string and choose a width:
SizeF sizeOfString = new SizeF();
sizeOfString = g.MeasureString("This is line 1" + Environment.NewLine + "This is line 2", f, 300);

//use:
Debug.WriteLine("String Height: " + sizeOfString.Height);
Debug.WriteLine("String Width: " + sizeOfString.Width);

For my examples, you’ll want to add the below to the top of your code:

using System.Diagnostics;
using System.Drawing;
C# – Measure String Width & Height (in pixels) Including Multi-line Text

Removing WordPress Spam Comments In Bulk

I’ve encountered a few WordPress blogs that had been bombarded with spam comments (some over 200k comments – taking several gigs of db space). Usually these comments accumulated over a few months or years (many using Akismet) – but if you’ve tried to delete thousands of comments through the WordPress admin, you might have noticed it taking very long and timing out.

Does your comments page look like this?
Does your comments page look like this?

 

Having these comments exist in your WordPress site only increases the size of the database, causing backups and migrations / upgrades to take longer. Unless you have plans to review thousands of comments (if so, you probably have too much time on your hands) you should be able to delete these in my opinion. The fastest option to remove the comments that I’ve found is to delete them from the database side (MySql).

Continue reading “Removing WordPress Spam Comments In Bulk”

Removing WordPress Spam Comments In Bulk

Creating a copy of your website using GNU Wget for Windows or OS X

There are times when you want to have a copy of your site (the frontend / user side). GNU Wget has been around a long time, but in my opinion, it’s still a great tool to backup / mirror websites.

Wget has many options and parameters, of which I won’t even scratch the surface, but below are the simple steps to get Wget setup and running on Windows and OSX machines. Wget is a command line utility, so it might appear overwhelming, but don’t worry, it’s cake!

Windows steps:

Step 1. Download / install Wget. Visit http://gnuwin32.sourceforge.net/packages/wget.htm and choose to download the Setup labeled “Complete package, except sources.”

Step 2. After installation is finished, open a command prompt (cmd.exe).

Step 3. Go to your GNU application folder (on 64 bit it’s in C:\Program Files (x86)\GnuWin32\bin, on 32 bit, it’s probably in C:\Program Files\GnuWin32\bin).

Step 4. To test if wget is installed correctly, run “wget -V“. It should return the current version and some credit. If not, revisit previous steps.

Step 5. To download / mirror a site, run  wget -e robots=off -r -l 0 -P “c:\\temp” http://www.chrisbitting.com – replacing “c:\temp” with the folder you want the site files to download to and “chrisbitting.com” with your site address.

wget_pc

 

You should now see the command prompt update with the progress – depending on the size of your site – it may take some time to download everything. After it’s finished, your directory should contact a mirror of your site, including html, css, images, etc.

 

Apple OS X steps:

Step 1. Open a blank terminal.

Step 2. Install homebrew by running:

ruby -e “$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)”

Step 3. After installing brew (and entering your password), run:

brew doctor

Step 4. Now install Wget using:

brew install wget

Step 5. When installation finishes, run “wget -V” to ensure Wget installed correctly. It should return the current version and some credit. If not, revisit previous steps.

Step 6. To download / mirror a site, run  wget -e robots=off -r -l 0 -P “/temp” http://www.chrisbitting.com – replacing “/temp” with the folder you want the site files to download to and “chrisbitting.com” with your site address.

wget_osx

You should now see the terminal update with the progress – depending on the size of your site – it may take some time to download everything. After it’s finished, your folder should contact a mirror of your site, including html, css, images, etc.

To see the multitude of options Wget provides, run “wget –help“. Happy downloading!

 

Creating a copy of your website using GNU Wget for Windows or OS X

Creating a .bash_profile file in OS X and adding PATH directories

If you’re starting out with a fresh install of OS X (10.9 in my example) and are using any development tools, at some point I’m sure you’ll want to add some directories to your system PATH. In short: this allows you to use an application in a specific directory from any other directory – commonly when you’re running commands in Terminal.

To start, we’ll utilize a text editor – in my case I’m using TextMate – but any plain text editor should do. Let’s get to it:

  1. bash_1Let’s first make sure you don’t already have a .bash_profile. In TextMate, go to File > Open. Browse to your home folder (with the house icon) and click “Show Hidden Files”. In your home folder you shouldn’t already see a .bash_profile file. (If you do, then you don’t need to create a new file and can open your file, make changes and skip to step 5.)
  2. bash_2So cancel the open dialog and enter some text into the untitled file currently open. You’re usually entering something like: export PATH=${PATH}:/somedirectory/asubdirectory:/anotherdirectory
  3. bash_3Now let’s save our new .bash_profile. Go to File > Save As. Browse to your home folder (with the little house icon again). Enter the filename as “.bash_profile” (without quotes).
  4. bash_4If you get a message saying “names that begin with a dot are reserved for the system” chose “Use ‘.’
  5. bash_5That’s it. Now if you already have a terminal open run source ~/.bash_profile (this just give you access to the updated PATH).
Creating a .bash_profile file in OS X and adding PATH directories

Upgrading Apache Cordova to latest version

Updating Apache Cordova to the latest version only takes a few minutes. Below are the steps (taken from Cordova) needed to update your > 3.0 projects to the latest version (3.5 as of this post).

In a command / terminal run:

  1. npm install -g cordova
  2. Now go to into your project (ie: c:\projects\test)  and run:
  3. cordova platform update android (or ios)

 

You can update your plugins by removing / adding them again like:

cordova plugin rm org.apache.cordova.file
cordova plugin add org.apache.cordova.file

That’s it. Now you should have an up to date environment.

Upgrading Apache Cordova to latest version

Local web server for testing / development using Node.js and http-server

localhost8080If you’re developing html / javascript applications and want to test locally, many times you will go beyond what local file access (file:///C:/…) in browsers will allow (like XMLHttpRequests, json calls, cross domain access and Access-Control-Allow-Origin restrictions).

A simple solution instead of deploying your code to apache or IIS is to install a local http server. http-server for Node.js is a fast, easy install and app that will allow you to use any directory as a http://localhost.

Installing this simple http server only takes a few steps:

  1. Install node.js if you don’t already have installed (from http://nodejs.org)
  2. In a command prompt / terminal, now run:
    npm install http-server -g
    

    (this installs http-server globally so you can access from any folder or directory)

  3. Now using command prompt or terminal, browser to a folder with some html you want to serve as http. (ie: c:\someproject\).
  4. Run:
    http-server
    
  5. Open your browser and visit http://localhost:8080.

 

You can change port 8080 (the default) to anything using “-p”, so http-server -p 8088 would change your local site to http://localhost:8088

Run http-server –help to see the other options available for running.

Local web server for testing / development using Node.js and http-server