adamkdean

software engineering

Proper sorting with JavaScript

By Adam K Dean on

If we're trying to sort an array, we're going to have a bad time. JavaScript doesn't do a good job of sorting out of the tin, so we're going to have to implement our own sorting algorithm. It's not really advanced enough to use the term algorithm, but let's get to it.

First, let's take an array. We can either have numbers, numbers in strings, or strings. Regardless of whether we have numbers in strings or actual numbers, the sorting will still be back to front by default. For example, [1, 8, 10, 12] will still become [1, 10, 12, 8] when we run sort().

So let's get that array.

var list = ['10', '12', '14', '16', '18', '20', '8'];

So now we have an array. It isn't sorted how you'd expect it to be. If we run sort(), we'll end up with a peculiar result. 10 will come before 8, as will 20, all the way up to 7...

// what we'll see
["10", "12", "14" "16", "18", "20", "8"]

// what we want to see
["8", "10", "12", "14", "16", "18", "20"]

To sort this, we're going to have to write our own sorting algorithm. We need to account for strings, numbers inside strings, numbers. The good thing about JavaScript sort() is that you can pass your own predicate. A predicate is an expression which returns either true or false. Is a greater than b?

A simple way to perform a numerical sort is:

list.sort(function (a, b) {
    return a - b;
});

But this isn't going to work properly for strings. A more advanced way of sorting it, which will account for strings as well, will check whether numbers are involved, and if so, convert the strings to numbers before comparing.

list.sort(function (a, b) {
    var ai = parseFloat(a), bi = parseFloat(b);
    return (isNaN(ai) || isNaN(bi)) 
        ? a > b ? 1 : a < b ? -1 : 0
        : ai > bi ? 1 : ai < bi ? -1 : 0;
});

Using either of these predicates with numbers/numbers-in-strings will output what you'd expect:

["8", "10", "12", "14", "16", "18", "20"]

But if we're using letters, such as a list of bra cup sizes, the advanced predicate will come out on top. Let's take a look at another example. In this, we're going to use said list of bra cup sizes and sort them. We'll see how the numerical predicate falls on it's face, and how the combined predicate doesn't.

var list = ['FF', 'GG', 'F', 'DD', 'K', 'E', 'G', 'D', 'JJ', 'J', 'HH', 'KK', 'H'];

list.sort(function(a, b) { return a - b; });
// outputs: 
// ["FF", "H", "F", "DD", "K", "E", "GG", "D", "JJ", "J", "HH", "KK", "G"]

list.sort(function (a, b) {
    var ai = parseFloat(a), bi = parseFloat(b);
    return (isNaN(ai) || isNaN(bi)) 
        ? a > b ? 1 : a < b ? -1 : 0
        : ai > bi ? 1 : ai < bi ? -1 : 0;
});
// outputs: 
// ["D", "DD", "E", "F", "FF", "G", "GG", "H", "HH", "J", "JJ", "K", "KK"]

It would be interesting to know why sort() doesn't use something like this by default. Maybe this will be the subject of a further blog post.

Check if type exists in MSSQL

By Adam K Dean on

Snippet time. Check if a type exists in MSSQL with the following simple query:

IF TYPE_ID(N'[dbo].[udt_SomeCustomType]') IS NOT NULL
BEGIN
    -- type exists, do something here
END

And of course, the other way round:

IF TYPE_ID(N'[dbo].[udt_SomeCustomType]') IS NULL
BEGIN
    -- type does not exist, do something here
END

More SQL snippets to come.

Ordering and filtering objects with ng-repeat

By Adam K Dean on

AngularJS allows you to iterate over collections using the ng-repeat directive. You have the ability to order and filter the collection, but this only works for arrays, not for objects. You'd think that you'd retain the functionality of arrays, considering the object is treated like one, but you don't.

The solution to this is to push the contents of the object into an array using a filter. By keeping the references intact, we are still able to bind to the objects, as they are essentially the same object.

.filter('objectAsArray', function() {
    return function(object) {
        var array = []; 
        for (item in object) {
            array.push(object[item]);
        }
        return array;
    }
});

Let's look at what we'd need if we wanted to order and/or filter an array:

<p ng-repeat="item in itemArray | orderBy: 'order' | filter: {visible: true}">
    {{item}}
</p>

But what if that was an object? Well, we just pop the objectAsArray filter in:

<p ng-repeat="item in itemObj | objectAsArray | orderBy: 'order' | filter: {visible: true}">
    {{item}}
</p>

This is indeed a very useful little filter.

View the live plunkr example here.

Giving the Squirt.io bookmarklet a favicon

By Adam K Dean on

Today I learnt about squirt.io, an open-source Spritz-like bookmarklet which helps you speed read the internet. From 250 words per minute all the way upto 950 words per minute. A bookmarklet is a javascript snippet which sits inside a bookmark on your "favourites bar", and allows you to execute that script on whichever website you're currently on.

I use Chrome mainly, and the only bug bear I have with bookmarklets is that they don't have favicons. As somebody who has a 1920 pixels of favicons stretching across the top of my browser, I can't stand to see that default white icon polluting the line of beautifully crafted icons.

There is a way to set the icon though!

Make sure you have the bookmarklet saved to your bookmarks bar. Then, go to squirt.io and bookmark it. Right click that bookmark, go to Bookmark manager. In the top left hand corner you'll see a menu Organise. Click it, and export your bookmarks to a HTML file.

Now open it up in your favourite text editor. I'm using Sublime. Search for squirt.io and you should find a line near the bottom of the file. It'll have a HREF pointing to squirt.io, an ADD_DATE, and more importantly, an ICON attribute.

<DT><A HREF="http://www.squirt.io/" ADD_DATE="1394700500" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABRElEQVQ4jZXSPUtcURAG4MeY+BVi0EJsBD8gRZogsk0Eu8XKRiSQwgj5CbYRLGyTKk0gTZoUsfCjEMFS/4EgRME2WwXUoEHN7qa4czfXu3dZM3A458y8874z5wzF1hH7BzzP+dpaCiyjjvW4P/gfgl4coRokC/clSQErkXiFW5yiX5s20uAozoMgu9ZyuJbqnyOhgi/YilYuMNaKJHU8w298x3gmPhekn3JiTeofA/imQGkT1xjJxtLEGgbwKs4/IjaMoTgf4xFe5wk6w1HOgF8GUR+6I16KpPnA1NLSHsa+KnmsJclP9GTKfxK+97jE43zvIkDy6rN4ireYwSSmsI1f+JPJazzUi6igEv3CLpYxIZmNM3zL5d25fJX8QhU7OMEB9v0bqFJB9Q2SQexpnsJ6lL5YpJ63LrzDIW7wExuYLlL+C3jqSJn7EH8tAAAAAElFTkSuQmCC">Squirt</A>

Above this bookmark, you should see your bookmarklet, where the HREF starts with javascript:. Notice how it doesn't have an ICON attribute set? Well, let's set that. Extract the ICON attribute from the squirt.io bookmark and inject it into the bookmarklet, like so:

<DT><A HREF="javascript:(function(){if(window.sq){window.sq.closed&&window.document.dispatchEvent(new Event('squirt.again'));}else{window.sq={};window.sq.userId='63c3f43e-7a2e-4652-ae1b-c8b309fbad5f';s=document.createElement('script');s.src='http://www.squirt.io/bm/squirt.js';s.s=window.location.search;s.idx=s.s.indexOf('sq-dev');if(s.idx!=-1){s.ampIdx=s.s.indexOf('&');s.host=s.s.substring(s.idx+7,s.ampIdx==-1?s.s.length:s.ampIdx);s.src='http://'+(s.host?s.host:'localhost')+':4000/bm/squirt.js';}document.body.appendChild(s);}})();" ADD_DATE="1394699165" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABRElEQVQ4jZXSPUtcURAG4MeY+BVi0EJsBD8gRZogsk0Eu8XKRiSQwgj5CbYRLGyTKk0gTZoUsfCjEMFS/4EgRME2WwXUoEHN7qa4czfXu3dZM3A458y8874z5wzF1hH7BzzP+dpaCiyjjvW4P/gfgl4coRokC/clSQErkXiFW5yiX5s20uAozoMgu9ZyuJbqnyOhgi/YilYuMNaKJHU8w298x3gmPhekn3JiTeofA/imQGkT1xjJxtLEGgbwKs4/IjaMoTgf4xFe5wk6w1HOgF8GUR+6I16KpPnA1NLSHsa+KnmsJclP9GTKfxK+97jE43zvIkDy6rN4ireYwSSmsI1f+JPJazzUi6igEv3CLpYxIZmNM3zL5d25fJX8QhU7OMEB9v0bqFJB9Q2SQexpnsJ6lL5YpJ63LrzDIW7wExuYLlL+C3jqSJn7EH8tAAAAAElFTkSuQmCC">Squirt</A>

Now save that file, go back to your Bookmark manager in Chrome, and import the file. Your bookmarklet will now be beautiful and all will be well with the world. Thanks again to Cameron Boehmer for this amazing bookmarklet, and to Readability and Spritz Inc too for their contributions!

Make Google look good again

By Adam K Dean on

If you're like me, you're probably feeling like your Wednesday has been ruined by Google testing bigger, none underlined titles for page results. It looks horrible, and is definitely a bad decision.

Bad Google!

There is a fix to tide you over though, in the case that Google drags their heels. If you're using Chrome, that is. Install Stylish (an extension which allows you to override a site's style), and install any theme for Google. Then edit it, delete all the existing css, and paste this in:

#res h3 { 
  font-size: 16px !important; 
}
#newsbox, .rgsep { 
  display: none;
}

This will make your page titles more bearable and remove the giant newsbox from the SERP.

Looking much better

Looking much better!