5
\$\begingroup\$

I've used the typical INT array sort function found all over the internet for years, but I've also had a need to sort other items in an array, or even to just simple take an Array of multiple types of items and get it in order for later readability. Thus leading me to the smartSort method I list below. If I've anything else I can think to implement at the moment (will update later), it's that I currently don't have a parameter for sorting Objects by a specific property.

The method (what work it does)

At current, it will sort a variety of items in an array; Ascending or Descending. If there are more than one type of item, then the types are grouped together in a somewhat (at least to me) logical fashion: Ints, Strings, Arrays, Unspecified Objects, Element Objects, Date Objects, nulls & undefines. However, within each group, the items are still sorted based on given parameter.

The simplicity is in only needing to supply 1 to 2 parameters (based on implementation, pure method form needs supplied Array of course). The main parameter is simply a string being 'asc' or 'desc'.

Implementation

Comes in 2 flavors. One, requiring EcmaScript 5.1, simply adds it to Array.prototype. Thus making it naturally easy to call, such as: myArray.smartSort('asc').

The other is a traditional method, of course. Old fashioned calling like: smartSort(myArray, 'asc').

Added Ease

Also implemented in both flavors is the ease of use on the String parameter. If no parameter is provided, than 'asc' is assumed.

Also implemented in flavor 2 is the ease of parameter order. It doesn't matter if you place the Array or the String first, as long as, at the least, the Array exist.

More Perks

Of course, simple things like Ints and Strings are handled in proper manner, including real number sorting as opposed to traditional .sort of JS. However, this will also sort Arrays and Objects based on items/properties, after sorting those accordingly. For example, if you have 3 arrays that all look like [ 'a', 'b' ]', but thenpush` a different string on each one, they will be sorted based on the 3rd string. Same for objects and their properties. The one exception, Dates Objects, are sorted by their date given date.

Array.prototype.smartSort('asc|desc')

if (Object['defineProperty'] && !Array.prototype['smartSort']) {
    var smartSortAsc = function(a, b) {
        if (a == null || a == undefined) return 1;
        if (b == null || b == undefined) return -1;
        var c = typeof a, d = typeof b, e = c + d;
        if (/^numbernumber$/ig.test(e)) return a - b;
        if (/^stringstring$/ig.test(e)) return a > b;
        if (/(string|number){2}/ig.test(e)) return /string/i.test(c) ? 1 : -1;
        if (/number/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1;
        if (/string/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1;
        if (/^objectobject$/ig.test(e)) {
            if (a instanceof Array) a.smartSort('a');
            if (b instanceof Array) b.smartSort('a');
            if (a instanceof Date && b instanceof Date) return a - b;
            if (a instanceof Array && b instanceof Array) {
                var f = Object.keys(a),
                    g = Object.keys(b),
                    h = f.concat(g).smartSort('a');
                for (var x in h) {
                    var i = h[x];
                    if (a[i] != b[i]) {
                        var j = [ a[i], b[i] ].smartSort('a');
                        return a[i] == j[0] ? -1 : 1;
                    }
                }
                var k = [ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ].smartSort('a');
                return a[Object.keys(a)[0]] == k[0] ? -1 : 1;
            }
            if (a instanceof Element && b instanceof Element) {
                if (a.tagName == b.tagName) {
                    var f = [ a.id, b.id ].smartSort('a');
                    return a.id == f[0] ? 1 : -1;
                }
                var f = [ a.tagName, b.tagName ].smartSort('a');
                return a.tagName == f[0] ? 1 : -1;
            }
            if (a instanceof Date || b instanceof Date) return a instanceof Date ? 1 : -1;
            if (a instanceof Array || b instanceof Array) return a instanceof Array ? -1 : 1;
            var f = Object.keys(a),
                g = Object.keys(b),
                h = f.concat(g).smartSort('a');
            for (var i = 0; i < 20; i++) {
                var j = f[i], k = g[i];
                if (a.hasOwnProperty(j) && b.hasOwnProperty(k)) {
                    if (a[j] instanceof Element && b[k] instanceof Element) {
                        if (a[j].tagName == b[k].tagName) {
                            var l = [ a[j].id, b[k].id ].smartSort('a');
                            return a[j].id == l[0] ? -1 : 1;
                        }
                        var l = [ a[j].tagName, b[k].tagName ].smartSort('a');
                        return a[j].tagName == l[0] ? -1 : 1;
                    }
                    if (a[j] instanceof Element || b[k] instanceof Element) return a[j] instanceof Element ? 1 : -1;
                    if (a[j] != b[k]) {
                        var m = [ a[j], b[k] ].smartSort('a');
                        return a[j] == m[0] ? -1 : 1;
                    }
                }
                if (a.hasOwnProperty(j) && a[j] instanceof Element) return 1;
                if (b.hasOwnProperty(k) && b[k] instanceof Element) return -1;
                if (!a.hasOwnProperty(j)) return -1;
                if (!b.hasOwnProperty(j)) return 1;
            }
            var m = [ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ].smartSort('a');
            return a[Object.keys(a)[0]] == m[0] ? 1 : -1;
        }
        var g = [a, b].sort();
        return g[0] > g[1];
    }
    var smartSortDesc = function(a, b) {
        if (a == null || a == undefined) return 1;
        if (b == null || b == undefined) return -1;
        var c = typeof a, d = typeof b, e = c + d;
        if (/^numbernumber$/ig.test(e)) return b - a;
        if (/^stringstring$/ig.test(e)) return b > a;
        if (/(string|number){2}/ig.test(e)) return /string/i.test(c) ? 1 : -1;
        if (/number/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1;
        if (/string/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1; 
        if (/^objectobject$/ig.test(e)) {   //  both are OBJECT
            if (a instanceof Array) a.smartSort('d');
            if (b instanceof Array) b.smartSort('d');
            if (a instanceof Date && b instanceof Date) return b - a;
            if (a instanceof Array && b instanceof Array) { //  both are ARRAY
                var f = Object.keys(a),
                    g = Object.keys(b),
                    h = f.concat(g).smartSort('a');
                for (var x in h) {
                    var i = h[x];
                    if (a[i] != b[i]) {
                        var j = [ a[i], b[i] ].smartSort('d');
                        return a[i] == j[0] ? -1 : 1;
                    }
                }
                var k = [ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ].smartSort('d');
                return a[Object.keys(a)[0]] == k[0] ? -1 : 1;
            }
            if (a instanceof Element && b instanceof Element) { //  both are ELEMENT
                if (a.tagName == b.tagName) {
                    var f = [ a.id, b.id ].smartSort('d');
                    return a.id == f[0] ? -1 : 1;
                }
                var f = [ a.tagName, b.tagName ].smartSort('d');
                return a.tagName == f[0] ? -1 : 1;
            }
            if (a instanceof Date || b instanceof Date) return a instanceof Date ? 1 : -1;
            if (a instanceof Array || b instanceof Array) return a instanceof Array ? -1 : 1;
            var f = Object.keys(a),
                g = Object.keys(b),
                h = f.concat(g).smartSort('a');
            for (var i = 0; i < 20; i++) {
                var j = f[i], k = g[i];
                if (a.hasOwnProperty(j) && b.hasOwnProperty(k)) {
                    if (a[j] instanceof Element && b[k] instanceof Element) {
                        if (a[j].tagName == b[k].tagName) {
                            var l = [ a[j].id, b[k].id ].smartSort('d');
                            return a[j].id == l[0] ? -1 : 1;
                        }
                        var l = [ a[j].tagName, b[k].tagName ].smartSort('d');
                        return a[j].tagName == l[0] ? -1 : 1;
                    }
                    if (a[j] instanceof Element || b[k] instanceof Element) return a[j] instanceof Element ? 1 : -1;
                    if (a[j] != b[k]) {
                        var m = [ a[j], b[k] ].smartSort('d');
                        return a[j] == m[0] ? -1 : 1;
                    }
                }
                if (a.hasOwnProperty(j) && a[j] instanceof Element) return 1;
                if (b.hasOwnProperty(k) && b[k] instanceof Element) return -1;
                if (!a.hasOwnProperty(j)) return 1;
                if (!b.hasOwnProperty(j)) return -1;
            }
            var m = [ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ].smartSort('d');
            return a[Object.keys(a)[0]] == m[0] ? -1 : 1;
        }
        var g = [a, b].sort();
        return g[0] < g[1];
    }
    Object.defineProperty(Array.prototype, 'smartSort', {
        value: function() {
            if (arguments && (!arguments.length || arguments.length == 1 && /^a([sc]{2})?$|^d([esc]{3})?$/i.test(arguments[0])))
                return this.sort(!arguments.length || /^a([sc]{2})?$/i.test(arguments[0]) ? smartSortAsc : smartSortDesc);
            return this.sort();
        }
    });
}

jsFiddle

smartSort(Array, 'asc|desc')

if (!window['smartSort']) {
    window.smartSort = function() {
        if (arguments) {
            var a, b;
            for (var x in arguments) arguments[x] instanceof Array && (a = arguments[x], void 0 == b && (b = "a")), "string" == typeof arguments[x] && (b = /^a([sc]{2})?$/i.test(arguments[x]) ? "a" : "d");
            if (a instanceof Array) return a.sort(b == 'a' ? smartSort.asc : smartSort.desc);
        }
        return this.sort();
    }
    smartSort.asc = function(a, b) {
        if (a == null || a == undefined) return 1;
        if (b == null || b == undefined) return -1;
        var c = typeof a, d = typeof b, e = c + d;
        if (/^numbernumber$/ig.test(e)) return a - b;
        if (/^stringstring$/ig.test(e)) return a > b;
        if (/(string|number){2}/ig.test(e)) return /string/i.test(c) ? 1 : -1;
        if (/number/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1;
        if (/string/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1;
        if (/^objectobject$/ig.test(e)) {
            if (a instanceof Array) a.sort(smartSort.asc);
            if (b instanceof Array) b.sort(smartSort.asc);
            if (a instanceof Date && b instanceof Date) return a - b;
            if (a instanceof Array && b instanceof Array) {
                var f = Object.keys(a),
                    g = Object.keys(b),
                    h = smartSort(f.concat(g), 'a');
                for (var x in h) {
                    var i = h[x];
                    if (a[i] != b[i]) {
                        var j = smartSort([ a[i], b[i] ], 'a');
                        return a[i] == j[0] ? -1 : 1;
                    }
                }
                var k = smartSort([ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ], 'a');
                return a[Object.keys(a)[0]] == k[0] ? -1 : 1;
            }
            if (a instanceof Element && b instanceof Element) {
                if (a.tagName == b.tagName) {
                    var f = smartSort([ a.id, b.id ], 'a');
                    return a.id == f[0] ? 1 : -1;
                }
                var f = smartSort([ a.tagName, b.tagName ], 'a');
                return a.tagName == f[0] ? 1 : -1;
            }
            if (a instanceof Date || b instanceof Date) return a instanceof Date ? 1 : -1;
            if (a instanceof Array || b instanceof Array) return a instanceof Array ? -1 : 1;
            var f = Object.keys(a),
                g = Object.keys(b),
                h = smartSort(f.concat(g), 'a');
            for (var i = 0; i < 20; i++) {
                var j = f[i], k = g[i];
                if (a.hasOwnProperty(j) && b.hasOwnProperty(k)) {
                    if (a[j] instanceof Element && b[k] instanceof Element) {
                        if (a[j].tagName == b[k].tagName) {
                            var l = smartSort([ a[j].id, b[k].id ], 'a');
                            return a[j].id == l[0] ? -1 : 1;
                        }
                        var l = smartSort([ a[j].tagName, b[k].tagName ], 'a');
                        return a[j].tagName == l[0] ? -1 : 1;
                    }
                    if (a[j] instanceof Element || b[k] instanceof Element) return a[j] instanceof Element ? 1 : -1;
                    if (a[j] != b[k]) {
                        var m = smartSort([ a[j], b[k] ], 'a');
                        return a[j] == m[0] ? -1 : 1;
                    }
                }
                if (a.hasOwnProperty(j) && a[j] instanceof Element) return 1;
                if (b.hasOwnProperty(k) && b[k] instanceof Element) return -1;
                if (!a.hasOwnProperty(j)) return -1;
                if (!b.hasOwnProperty(j)) return 1;
            }
            var m = smartSort([ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ], 'a');
            return a[Object.keys(a)[0]] == m[0] ? 1 : -1;
        }
        var g = [a, b].sort();
        return g[0] > g[1];
    }
    smartSort.desc = function(a, b) {
        if (a == null || a == undefined) return 1;
        if (b == null || b == undefined) return -1;
        var c = typeof a, d = typeof b, e = c + d;
        if (/^numbernumber$/ig.test(e)) return b - a;
        if (/^stringstring$/ig.test(e)) return b > a;
        if (/(string|number){2}/ig.test(e)) return /string/i.test(c) ? 1 : -1;
        if (/number/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1;
        if (/string/ig.test(e) && /object/ig.test(e)) return /object/i.test(c) ? 1 : -1; 
        if (/^objectobject$/ig.test(e)) {   //  both are OBJECT
            if (a instanceof Array) a.sort(smartSort.desc);
            if (b instanceof Array) b.sort(smartSort.desc);
            if (a instanceof Date && b instanceof Date) return b - a;
            if (a instanceof Array && b instanceof Array) { //  both are ARRAY
                var f = Object.keys(a),
                    g = Object.keys(b),
                    h = smartSort(f.concat(g), 'a');
                for (var x in h) {
                    var i = h[x];
                    if (a[i] != b[i]) {
                        var j = smartSort([ a[i], b[i] ], 'd');
                        return a[i] == j[0] ? -1 : 1;
                    }
                }
                var k = smartSort([ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ], 'd');
                return a[Object.keys(a)[0]] == k[0] ? -1 : 1;
            }
            if (a instanceof Element && b instanceof Element) { //  both are ELEMENT
                if (a.tagName == b.tagName) {
                    var f = smartSort([ a.id, b.id ], 'd');
                    return a.id == f[0] ? -1 : 1;
                }
                var f = smartSort([ a.tagName, b.tagName ], 'd');
                return a.tagName == f[0] ? -1 : 1;
            }
            if (a instanceof Date || b instanceof Date) return a instanceof Date ? 1 : -1;
            if (a instanceof Array || b instanceof Array) return a instanceof Array ? -1 : 1;
            var f = Object.keys(a),
                g = Object.keys(b),
                h = smartSort(f.concat(g), 'a');
            for (var i = 0; i < 20; i++) {
                var j = f[i], k = g[i];
                if (a.hasOwnProperty(j) && b.hasOwnProperty(k)) {
                    if (a[j] instanceof Element && b[k] instanceof Element) {
                        if (a[j].tagName == b[k].tagName) {
                            var l = smartSort([ a[j].id, b[k].id ], 'd');
                            return a[j].id == l[0] ? -1 : 1;
                        }
                        var l = smartSort([ a[j].tagName, b[k].tagName ], 'd');
                        return a[j].tagName == l[0] ? -1 : 1;
                    }
                    if (a[j] instanceof Element || b[k] instanceof Element) return a[j] instanceof Element ? 1 : -1;
                    if (a[j] != b[k]) {
                        var m = smartSort([ a[j], b[k] ], 'd');
                        return a[j] == m[0] ? -1 : 1;
                    }
                }
                if (a.hasOwnProperty(j) && a[j] instanceof Element) return 1;
                if (b.hasOwnProperty(k) && b[k] instanceof Element) return -1;
                if (!a.hasOwnProperty(j)) return 1;
                if (!b.hasOwnProperty(j)) return -1;
            }
            var m = smartSort([ a[Object.keys(a)[0]], b[Object.keys(b)[0]] ], 'd');
            return a[Object.keys(a)[0]] == m[0] ? -1 : 1;
        }
        var g = [a, b].sort();
        return g[0] < g[1];
    }
}

jsFiddle

Other than the obvious implementation of an alternate param for sorting by a specific Object Property, what could I improve to make it more flexible or even extend its functionality?

\$\endgroup\$

locked by 200_success Apr 7 '16 at 17:12

This post has been locked while disputes about its content are being resolved. You may discuss this on meta if you have concerns.

Read more about locked posts here.

  • 4
    \$\begingroup\$ I have rolled back the last edit. Please see What to do when someone answers. \$\endgroup\$ – Ethan Bierlein Apr 7 '16 at 16:41
  • \$\begingroup\$ @EthanBierlein That defeats the purpose. I didn't like any of the answers, never used them and still have the same question for new code. no point in making a new question. I'd delete this old one if i could, it's useless. i will continue to change it as long as i can, it's my question. \$\endgroup\$ – user10934 Apr 7 '16 at 16:47
  • 2
    \$\begingroup\$ @SpYk3HH I understand that you may feel frustrated, but civil language is required at all times on Stack Exchange sites. Your Rev 5 (and subsequent odd-numbered revisions) completely changes the code. If you would like the new code reviewed, ask a separate question (optionally mentioning this one). \$\endgroup\$ – 200_success Apr 7 '16 at 17:21
7
\$\begingroup\$

Naming

Using single-letter variable names is bad, and there is absolutely no reason to use them as we aren't limited on name length anymore. Looking through your code, I still don't know what a, b, i, j, m, and the like mean, what they are, or what they do. Using descriptive names would tell me what they do so I could understand the code even if I didn't know JavaScript. Also, if you use good names, you won't need to give a comment stating what the variable is, does, or contains. Good code doesn't need a whole lot of comments. In fact, most of the comments I see are like this:

catch
{
    // gotcha!
}

Sorting

var l = [ a[j].id, b[k].id ].smartSort('a');
var f = [ a.tagName, b.tagName ].smartSort('d');

I assume 'a' and 'd' are specifying which way to sort the collection of items. You should avoid passing text values to specify what your program should do. In this case, I would pass a Boolean value, but you can define enums in JavaScript too.

Functions

You don't need to all related code in one function. You should split your code up into more functions. This also makes it easier to pinpoint bugs, fix them without introducing more bugs, and helps prevent violation of SRP and creation of megamoths.

\$\endgroup\$
3
\$\begingroup\$

I do not get the reason for having two functions for asc and desc ordering. In principle, if asc is a call to a function sort(a,b), then desc is nothing more than calling the same function with reverse order of parameters sort(b,a). No need for code duplication.

As far as I can see, the only difference beside calling your sort function with different parameters is, that you in the asc-Version have lines, which contain .smartSort('a') and the desc-Version calles .smartSort('d'). You could refactor this out into a call sort(a,b,"a") resp. sort(b,a,"d").

Besides that this code has to be broken down into smaller easier to understand sections - I really do not want to get my head around this - you have to avoid code duplication by all means, since it is the N°1 source of errors: if you change a statement in one place, you have to make sure, it is changed in every further place.

\$\endgroup\$