1

I have a javascript array which looks like this:

var data =  ["size64_tolPercent0.01_best", "size256_tolPercent0.01_best", "size256_tolPercent0_worst", "sizemax_tolPercent0_worst", "size1518_tolPercent0_worst", "size64_tolPercent0.01_worst", "size512_tolPercent0.01_worst", "size1518_tolPercent0.01_worst", "size256_tolPercent0_best"] etc

I want to sort this array :

  1. by the substring "best" and "worst".
  2. by the tolPercents 0 and 0.01
  3. sizes (64, 256, 512, 1518, max).. it would be good if the sizes are sorted by strings since it contains a "max" keyword..

So, we end up with a result array which looks like this:

["size64_tolPercent0_best", "size256_tolPercent0_best", "size512_tolPercent0_best", "size1518_tolPercent0_best", "sizemax_tolPercent0_best", "size64_tolPercent0.01_best", "size512_tolPercent0.01_best", "size1518_tolPercent0.01_best", "size64_tolPercent0_worst", "size256_tolPercent0_worst", "size512_tolPercent0_worst", "size1518_tolPercent0_worst", "sizemax_tolPercent0_worst"] etc

I am able to sort the strings using one of the methods but not all.

Here's what I've tried so far, I'm doing it wrong I'm sure, just need some help in the right direction. Doing it for 1 and 3 now:

var someLargeValue = 10000000;
data.sort(function(x,y){
  var xp = x.substr(getPosition(x, '_', 2) + 1, x.split("_").pop().length);
  var yp = y.substr(getPosition(y, '_', 2) + 1, y.split("_").pop().length);
  return xp == yp ? 0 : xp < yp ? -1 : 1;
});
data.sort(function(x,y){
  var xp = x.substr(4, getPosition(x, '_', 1) - 4);
  var yp = y.substr(4, getPosition(y, '_', 1) - 4);
  if(xp === "max") {
    xp = someLargeValue;
  }
  if(yp === "max") {
    yp = someLargeValue;
  }
  xp = parseInt(xp);
  yp = parseInt(yp);

  return xp == yp ? 0 : xp < yp ? -1 : 1;
});
function getPosition(str, m, i) {
    return str.split(m, i).join(m).length;
}

But i'm afraid the code i'm trying is doing the sorting sequentially.. so what the first custom sort method is overridden by the second, I think?

Any help much appreciated.

5
  • show us what you've tried so far Commented Sep 3, 2015 at 14:30
  • Have you tried anything? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Sep 3, 2015 at 14:31
  • Just use all three methods, one after another, depending on your priority. For example sorting by: best-worst first, then percent next, then size last. Note that your cannot accurately sort this without some sort of priority ranking. Commented Sep 3, 2015 at 14:32
  • I've edited the question with my code. Commented Sep 3, 2015 at 14:37
  • Yes you are right, the second sort overwrites the first one. One call is enough to get the desired result. See stackoverflow.com/a/32379540/1636522 and stackoverflow.com/a/32379343/1636522. Commented Sep 3, 2015 at 15:44

3 Answers 3

2

This solution features the Sorting with map from MDN.

First build a mapped array with splitted items for sorting. Then sort it by the custom order.

function (a, b) {
    return a.rating.localeCompare(b.rating) ||
           a.tolPercents - b.tolPercents ||
           a.size - b.size;
}

Altogether:

var data = [
        "size64_tolPercent0.01_best",
        "size256_tolPercent0.01_best",
        "size256_tolPercent0_worst",
        "sizemax_tolPercent0_worst",
        "size1518_tolPercent0_worst",
        "size64_tolPercent0.01_worst",
        "size512_tolPercent0.01_worst",
        "size1518_tolPercent0.01_worst",
        "size256_tolPercent0_best"
    ],
    result = data.map(function (el, i) {
        var a = /^size(.+)_tolpercent(.+)_(.+)$/i.exec(el);
        return {
            index: i,
            rating: a[3],
            tolPercents: a[2],
            size: a[1] === 'max' ? Number.MAX_VALUE : a[1]
        };
    }).sort(function (a, b) {
        return a.rating.localeCompare(b.rating) || a.tolPercents - b.tolPercents || a.size - b.size;
    }).map(function (el) {
        return data[el.index];
    });

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the answer. What if the sizes are not known in advance?
2

I would first split the strings in order to minimize the amount of work done by the comparison function, as well as make it more readable. Then, I would recreate the strings once the array is sorted. Using a map is smarter though: https://stackoverflow.com/a/32379540/1636522 :-D

var data = ["size64_tolPercent0_best", "size256_tolPercent0_best", "size512_tolPercent0_best", "size1518_tolPercent0_best", "sizemax_tolPercent0_best", "size64_tolPercent0.01_best", "size512_tolPercent0.01_best", "size1518_tolPercent0.01_best", "size64_tolPercent0_worst", "size256_tolPercent0_worst", "size512_tolPercent0_worst", "size1518_tolPercent0_worst", "sizemax_tolPercent0_worst"];

// split the strings

var re = /max|\d+(?:\.\d+)?(?=_)|[^_]+$/g;

data = data.map(function (x) {
  x = x.match(re); // example: ["64", "0.01", "best"]
  if (x[0] !== 'max') x[0] = parseInt(x[0], 10);
  x[1] = parseFloat(x[1]);
  return x;
});

// sort the array

data.sort(function cmp (a, b) {
  // 1. "best" and "worst"
  if (a[2] > b[2]) return 1;
  if (a[2] < b[2]) return -1;
  // 2. percent
  if (a[1] > b[1]) return 1;
  if (a[1] < b[1]) return -1;
  // 3. size
  if (a[0] === 'max') return 1;
  if (b[0] === 'max') return -1;
  if (a[0] > b[0]) return 1;
  if (a[0] < b[0]) return -1;
  // 4. no differences
  return 0;
});

// recreate the strings

data = data.map(function (x) {
  x[0] = 'size' + x[0];
  x[1] = 'tolPercent' + x[1];
  return x.join('_');
});

// print the result

data.forEach(function (x) {
  document.write(x + '<br />');
});
* {font-family:Courier}

Comments

-1

The Array class has a sort function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Leverage the compare argument:

data.sort(function(a, b) { // Follow the docs as per how a relates to b, returning -1 (a < b),0 (equal) or 1 (a > b) });

Extract and compare the sub strings as required in the function.

Update
After reading your code that you added, don't call sort() twice, just incorporate all the comparisons into the same sort comparison. Stop at the first inequality (aValue !== bValue) and return that comparison.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.