-1

I would like to sort royal names. First Preference to the alphabet. If both names are same then I would like to sort by the roman numeral. For Example if input is : King III, King II, Queen IX. (as 1st 2 strings are same they need to be sorted by their appended roman numeral) So Expected output : King II, King III, Queen IX.

I tried storing roman numerals in a hashmap and writing a function that replaces roman numerals in given array to King 2, King 3, Queen 9 and then tried sorting but was not able to implement correctly. Could anyone kindly help me with this?

Sai
  • 245
  • 1
  • 3
  • 12

3 Answers3

6

function romanToNum(roman) {
  if (roman === "")           return 0;
  if (roman.startsWith("L"))  return 50 + romanToNum(roman.substr(1));
  if (roman.startsWith("XL")) return 40 + romanToNum(roman.substr(2));
  if (roman.startsWith("X"))  return 10 + romanToNum(roman.substr(1));
  if (roman.startsWith("IX")) return 9  + romanToNum(roman.substr(2));
  if (roman.startsWith("V"))  return 5  + romanToNum(roman.substr(1));
  if (roman.startsWith("IV")) return 4  + romanToNum(roman.substr(2));
  if (roman.startsWith("I"))  return 1  + romanToNum(roman.substr(1));
  return 0;
}
console.log(
    ["King III", "King II", "Queen IX"]
      .map((n) => ({name: n, num: romanToNum(n.split(" ").pop())}))
      .sort((a, b) => (a.num - b.num))
      .map(({name, num}) => name)
  );

romanToNum function adapted from this answer. The array is first mapped into {name, num} objects, then sorted on num, then converted back to names only.

Aurel Bílý
  • 7,068
  • 1
  • 21
  • 34
1

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

function customSort(data, order) {

    function isNumber(v) {
        return (+v).toString() === v;
    }

    function isRoman(s) {
        // http://stackoverflow.com/a/267405/1447675
        return /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/i.test(s);
    }

    function parseRoman(s) {
        var val = { M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 };
        return s.toUpperCase().split('').reduce(function (r, a, i, aa) {
            return val[a] < val[aa[i + 1]] ? r - val[a] : r + val[a];
        }, 0);
    }

    var sort = {
            asc: function (a, b) {
                var i = 0,
                    l = Math.min(a.value.length, b.value.length);

                while (i < l && a.value[i] === b.value[i]) {
                    i++;
                }
                if (i === l) {
                    return a.value.length - b.value.length;
                }
                if (isNumber(a.value[i]) && isNumber(b.value[i])) {
                    return a.value[i] - b.value[i];
                }
                if (isRoman(a.value[i]) && isRoman(b.value[i])) {
                    return parseRoman(a.value[i]) - parseRoman(b.value[i]);
                }
                return a.value[i].localeCompare(b.value[i]);
            },
            desc: function (a, b) {
                return sort.asc(b, a);
            }
        },
        mapped = data.map(function (el, i) {
            var string = el.replace(/\d(?=[a-z])|[a-z](?=\.)/gi, '$&. .'),
                regex = /(\d+)|([^0-9.]+)/g,
                m,
                parts = [];

            while ((m = regex.exec(string)) !== null) {
                parts.push(m[0]);
            }
            return { index: i, value: parts, o: el, string: string };
        });

    mapped.sort(sort[order] || sort.asc);
    return mapped.map(function (el) {
        return data[el.index];
    });
}

var arr = ['King III', 'King II', 'Queen IX'];

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; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

use your own convert table to get the decimal value of the roman numbers, than a simple sort with the proper callback

var romanNumberToDec = {
  "I" : 1, "II" : 2, "III" : 3, "IV" : 4, "V" : 5,
  "VI" : 6, "VII" : 7, "VIII" : 8, "IX" : 9, "X" : 10
}

ES6console with the table

if thats all you need to deal with

but if you need a more generic way - from selftaughtjs -

function fromRoman(str) {  
   var result = 0;
   // the result is now a number, not a string
   var decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
   var roman = ["M", "CM","D","CD","C", "XC", "L", "XL", "X","IX","V","IV","I"];
   for (var i = 0;i<=decimal.length;i++) {
       while (str.indexOf(roman[i]) === 0){
         result += decimal[i];
         str = str.replace(roman[i],'');
       }
   }
   return result;
}


var arrayObj = [ "King III", "King II", "Queen IX"];

function mySort() {
    arrayObj.sort( (a, b)=> {
      let aNum = a.substr(a.lastIndexOf(" ") + 1, a.length);
      let bNum = b.substr(b.lastIndexOf(" ") + 1, b.length);
      // return romanNumberToDec[aNum] - romanNumberToDec[bNum]; 
      return fromRoman(aNum) - fromRoman(bNum);
    });
    console.log(arrayObj);
};

mySort();

ES6console the generic way

Ofir G
  • 736
  • 6
  • 18