Sort an array of objects alphabetically using Underscore.js

I am facing a challenge with sorting an array of objects alphanumerically. Here is an example to illustrate my question:

var objs = {
    'obj1': {'name': 'Object21'},
    'obj2': {'name': 'Object140'},
    'obj3': {'name': 'Object28'},
    'obj4': {'name': 'Object251'}
};

After using

_.sortBy(objs, function(obj) { return obj.name; }
, the resulting order is:

  1. Object140
  2. Object21
  3. Object251
  4. Object28

I am looking for a way to achieve this alphanumerical sorting directly with Underscore without creating a separate array just for names. Is there a more efficient method to do this?

Answer №1

I have successfully found a solution to this issue on my own using Google :-) Here is the method I used for those who may need it in the future, known as "Natural Sorting"

To use, simply call

_.sortByNat(objs, function(obj) { return obj.name; })

/*
* Natural Sorting with Backbone.js & Underscore.js
*
* @author Kevin Jantzer <https://gist.github.com/kjantzer/7027717>
* @since 2013-10-17
* 
* NOTE: Make sure to include the Natural Sort algorithm by Jim Palmer (https://github.com/overset/javascript-natural-sort)
*/

// Extends _.sortByNat() method
_.mixin({

    sortByNat: function(obj, value, context) {
        var iterator = _.isFunction(value) ? value : function(obj){ return obj[value]; };
        return _.pluck(_.map(obj, function(value, index, list) {
          return {
            value: value,
            index: index,
            criteria: iterator.call(context, value, index, list)
          };
        }).sort(function(left, right) {
          var a = left.criteria;
          var b = right.criteria;
          return naturalSort(a, b);
        }), 'value');
    }
});

/*
* Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
* Author: Jim Palmer (based on chunking idea from Dave Koelle)
* https://github.com/overset/javascript-natural-sort
*/
function naturalSort (a, b) {
    var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
        sre = /(^[ ]*|[ ]*$)/g,
        dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
        hre = /^0x[0-9a-f]+$/i,
        ore = /^0/,
        i = function(s) { return naturalSort.insensitive && (''+s).toLowerCase() || ''+s },
        // Convert all to strings and strip whitespace
        x = i(a).replace(sre, '') || '',
        y = i(b).replace(sre, '') || '',
        // Chunk/tokenize
        xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
        yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
        // Numeric, hex or date detection
        xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
        yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
        oFxNcL, oFyNcL;
    // First try and sort Hex codes or Dates
    if (yD)
        if ( xD < yD ) return -1;
        else if ( xD > yD ) return 1;
    // Natural sorting through split numeric strings and default strings
    for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
        // Find floats not starting with '0', string or 0 if not defined (Clint Priest)
        oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
        oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
        // Handle numeric vs string comparison - number < string - (Kyle Adams)
        if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; }
        // Rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
        else if (typeof oFxNcL !== typeof oFyNcL) {
            oFxNcL += '';
            oFyNcL += '';
        }
        if (oFxNcL < oFyNcL) return -1;
        if (oFxNcL > oFyNcL) return 1;
    }
    return 0;
}

// Extend Array to have a natural sort function
Array.prototype.sortNat = function(){
    return Array.prototype.sort.call(this, naturalSort)
}

Answer №2

To achieve desired sorting of objects based on their names, you can create a custom iterator function and apply it like so:

var objects = {
    'obj1': {'name': 'Object21'},
    'obj2': {'name': 'Object140'},
    'obj3': {'name': 'Object28'},
    'obj4': {'name': 'AnObject251'}
};

_.sortBy(objects, function(object) {
    var charCodes = [], name = object.name;
    for(var i = 0, char; char = name.charAt(i); i++) 
        char == +char ? charCodes.push(+char) : charCodes.push(char.charCodeAt(0));
    return +charCodes.join('');
});

> Object21
  Object28
  Object140
  AnObject251

The object "AnObject251" is placed last due to its longer name.

Answer №3

If you are looking for efficient sorting, consider using the Alphanum sorting algorithm along with a refined JavaScript implementation:

function alphanum(a, b) {
  function segmentString(t) {
    var segments = [], x = 0, y = -1, numFlag = 0, i, j;

    while (i = (j = t.charAt(x++)).charCodeAt(0)) {
      var isNumber = (i == 46 || (i >= 48 && i <= 57));
      if (isNumber !== numFlag) {
        segments[++y] = "";
        numFlag = isNumber;
      }
      segments[y] += j;
    }
    return segments;
  }

  var segA = segmentString(a);
  var segB = segmentString(b);

  for (x = 0; segA[x] && segB[x]; x++) {
    if (segA[x] !== segB[x]) {
      var valA = Number(segA[x]), valB = Number(segB[x]);
      if (valA == segA[x] && valB == segB[x]) {
        return valA - valB;
      } else return (segA[x] > segB[x]) ? 1 : -1;
    }
  }
  return segA.length - segB.length;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Locating an inadequately labeled variable or object

Dealing with approximately 100 files written in Python, JavaScript, and HTML has presented me with a challenging task. Many of these files are interconnected in intricate ways, making it difficult for me to trace back the origin of variables or objects. I ...

Is it possible to showcase the $timeout duration using a progress bar?

I'm considering showcasing the remaining time using my $timeout to notify individuals when their time is finished. Unfortunately, I haven't been able to locate any information about this online. Therefore, my query is... Is it feasible to displ ...

What is the process for reaching application-level middleware from the router?

Within my project created using express application generator, I am facing a challenge accessing my application-level middleware from the router. This middleware is specifically designed to query the database using the user ID that is received from the ro ...

Navigate through each nth-child element in the loop and then transition to the switch function

I am currently working on creating a table using a loop where the first cell has a variable value, and the rest of the cells in the row have the same value. Initially, I wrote out the code for this task, but now I want to optimize it using a more efficien ...

Execute the gulp module on the source files

Recently, I've been delving into the world of gulp and trying to enhance the readability of my js source files. I have a task in place (which executes successfully) that utilizes 'gulp-beautify' to beautify the js files: gulp.task('js& ...

Alert: A notification when navigating away from your site

Is there a way to notify users when they click on an external link that they are leaving your site? <div class="row"> <div class="col-lg-12"> <div class="form-group"> *If you need information on other applicable forms, you ...

Ensuring the server application is up and running before initiating the mocha tests

This is similar to Ensuring Express App is running before each Mocha Test , but the proposed solution isn't effective + I am utilizing a websocket server In essence, I'm making use of a websocket framework called socketcluster and this represent ...

Encountering a persistent Unhandled rejection Error while utilizing NodeJs with Bluebird library

Currently in the process of developing a daemon that listens to TCP connections, sends commands, and listens for events. I made the decision to utilize bluebird to eliminate callbacks, but I'm encountering an issue. I can't seem to catch a rejec ...

Choosing Select2: Customizing the context of formatSelection

I've created a simple custom wrapper for Select2, which has been very helpful. However, I am facing an issue with the formatSelection field. When initializing Select2 through my wrapper, it looks like this: this.elem.select2({ allowClear : option ...

"Placement of script tag within the body and the function of the appendChild method

What do you think will happen in the example provided below? The majority of current browsers (such as Firefox 5, Chrome 14, Opera 11.50) will place the iframe above the text. Is this behavior standardized or just a common practice that could potentially ...

Incorporate jQuery on elements that are dynamically loaded after the page has finished loading

I need to determine if a dynamically added button is enabled or disabled. How can this be achieved? My goal is to display a message when the button is disabled and remove it when the button becomes enabled. This is my current code: jQuery(document).read ...

What is the prescribed interface or datatype for symbol type in TypeScript with JavaScript?

I have a set of symbol values in JavaScript that I want to convert to TypeScript. // Defining object values in JavaScript const size = { Large: Symbol('large'), Medium: Symbol('medium') } What is the most efficient method to conv ...

Utilize Vue to showcase data from the RapidAPI interface

Experimenting with vueJS and rapidapi led me to a challenge: displaying data from an API using vue and the JS Fetch method. Unfortunately, all I could see when running the code was the initial value ([]). <template> <div> <div>{{ c ...

Paging in Ext JS does not function properly when using local data sources

I am facing an issue with enabling paging in ExtJs4 grid. The paging toolbar appears to be functioning correctly, however, the paging feature does not seem to work within the grid itself. Can anyone provide guidance on what might be missing? Ext.onReady(f ...

Date discrepancy detected on AWS EBS server; however, it is operating seamlessly on the local environment

Whenever deployed on an AWS server, there seems to be a recurring miscalculation. Background: All dates stored in my MongoDB are in UTC format. I need them converted to IST before exporting them to Excel. While my code functions flawlessly on my local m ...

Troubleshooting: Next JS 13/14 - Server component failing to update data upon revisiting without requiring a refresh

After attempting to retrieve the most recent liked items from a server component in next, I noticed that the data only displays when manually refreshing the page. Even when I navigate away and return, the data is not refetched automatically - however, usin ...

Alter the hue on the fly

How can I change the color of a div based on whether it is currently red or green? I attempted the following code, but it doesn't seem to be working as expected. if ($(this).css("background-color")=="rgb(34,187,69)"|| $(this).css("background-color") ...

How about mixing up your backgrounds with an overlay effect for a unique look?

Hey there, I'm currently working on adding random backgrounds to my website through an overlay, but I've hit a roadblock when it comes to displaying them. Here is the code I'm working with: .css / .php #intro { background: ...

Substitute the indexes and contents of the array

Think of a scenario where you have the following array: Array ( [0] => Array ( [id] => 45 [name] => Name1 [message] => Ololo [date_create] => 21:03:56 ) [1] => Array ( [id] => 46 [name] => visitor [message] => Hi! [date_create ...

Exploring 2D Array Zones Using Python

Imagine having the following 2d array: [ [1,1,1,2,2], [1,1,2,3,2], [2,2,2,3,1], [2,1,0,3,2], [2,0,3,3,0]] In this array, there are zones with the same values. If a zone has 5 or more cells, those values turn into zeros, resulting in this new array: [ [0 ...