Prozrachniy Prozrachniy - 1 month ago 5
Javascript Question

Sort array of objects with dots, letters, numbers. I was able to sort by numbers, but mixed values are difficult. Not sure if possible to do it right

I tried the typical sort function and checked if item is string. But I get a very strange output. Tried multiple different approaches.

var arr = [{section: '12.2.a'},
{section: '12.2.b.iii'},
{section: '12.2.c'},
{section: '12'},
{section: '12A'},
{section: '12.3.b'},
{section: '12.3.c'},
{section: 'Q2'},
{section: 'Q32'},
{section: 'Q6'},
{section: 'Q5'}]



var arr2 = arr.sort(function(a, b) {
var nums1 = a.section.split(".");
var nums2 = b.section.split(".");

for (var i = 0; i < nums1.length; i++) {
if (nums2[i]) {
if (nums1[i] !== nums2[i]) {
if (isNaN(parseInt(nums1[i])) && isNaN(parseInt(nums2[i]))) {
return nums1[i].localeCompare(nums2[i]);
}
return parseInt(nums1[i]) - parseInt(nums2[i]);
}
} else {
return 1;
}
}
return -1;
});


Should I use localeCompare or is it possible to without ?
Would like the output to be:

[
{section: '12'},
{section: '12A'},
{section: '12.2.a'},
{section: '12.2.b.iii'},
{section: '12.2.c'},
{section: '12.3.b'},
{section: '12.3.c'},
{section: 'Q2'},
{section: 'Q6'},
{section: 'Q5'}
{section: 'Q32'}]


Would much appreciate any suggestions

Answer

You could split the string and use sorting with map, while comparing each element of the one with eacvh element of the other one. if both elements are numbers, take the difference, otherwise return the result of localeCompare.

function customSort(d, order) {
    var sort = {
            asc: function (a, b) {
                var l = 0, m = Math.min(a.value.length, b.value.length);
                while (l < m && a.value[l] === b.value[l]) {
                    l++;
                }
                return l === m ?
                    a.value.length - b.value.length :
                    ((+a.value[l]).toString() === a.value[l] && (+b.value[l]).toString() === b.value[l]) ?
                        a.value[l] - b.value[l] :
                        a.value[l].localeCompare(b.value[l]
                    );
            },
            desc: function (a, b) {
                return sort.asc(b, a);
            }
        },

        // temporary array holds objects with position and sort-value
        mapped = d.map(function (el, i) {
            var regex = /(\d+)|([^0-9.]+)/g,
                m,
                parts = [];

            while ((m = regex.exec(el.section)) !== null) {
                // This is necessary to avoid infinite loops with zero-width matches
                if (m.index === regex.lastIndex) {
                    regex.lastIndex++;
                }
                parts.push(m[0]);
            }
            return { index: i, value: parts };
        });

    // sorting the mapped array containing the reduced values
    mapped.sort(sort[order] || sort.asc);

    // container for the resulting order
    return mapped.map(function (el) {
        return d[el.index];
    });
}

var arr = [{ section: '12.2.a' }, { section: '12.2.b.iii' }, { section: '12.2.c' }, { section: '12' }, { section: '12A' }, { section: '12.3.b' }, { section: '12.3.c' }, { section: 'Q2' }, { section: 'Q32' }, { section: 'Q6' }, { section: 'Q5' }];

console.log('sorted array asc', customSort(arr));
console.log('sorted array desc', customSort(arr, 'desc'));
console.log('original array', arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }