5
\$\begingroup\$

if I have a string AYUKB17053UI903TBC. I want a function to return ABKUY01357IU039BCT. So every alphabetical part of the string is sorted, as well as the numerical part. But they retain their original orders in the string...

We can assume that the input only contains number and English letters

I came up with a solution but I don't think it is elegant.

    const string1 = 'AYUKB17053UI903TBC'
    const string2  = `ABKUY01357IU039BCT`
    
    
    function fn1(string1) {
      let tempArray = [[]]
      for (const char of string1) {
        let lastCharIsNumber
        let currentCharIsNumber
        const lastArray = tempArray[tempArray.length - 1]
        if(!lastArray.length){
          lastArray.push(char)
          continue
        }
        currentCharIsNumber = !Number.isNaN(Number(char))
        lastCharIsNumber = !Number.isNaN(Number(lastArray[lastArray.length - 1]))
        if (currentCharIsNumber && lastCharIsNumber)  lastArray.push(char)
        else if (!currentCharIsNumber && !lastCharIsNumber) lastArray.push(char)
        else tempArray.push([char])
      }
    
      tempArray.forEach(item => item.sort())
    
      return tempArray.map(array => array.join('')).join('')
    }
    
    console.log(fn1(string1) === string2); // true

Can anyone help to improve my solution?

\$\endgroup\$
0

2 Answers 2

4
\$\begingroup\$

I'm not sure exactly what your metric is for being "elegant", but here's a bit of a different approach that I think is fairly "clean" and "simple" to follow:

const string1 = 'AYUKB17053UI903TBC';
const string2  = 'ABKUY01357IU039BCT';

function sortPieces(str) {
    const piecesArray = [];
    let lastPiece = [];
    let lastType;
    for (const char of str) {
        const nextType = (char >= "0" && char <= "9") ? "number" : "letter";
        if (nextType === lastType || !lastType) {
            // either same type as previous char or first char in string
            lastPiece.push(char);
        } else {
            // different type of char than previous char, start a new piece
            piecesArray.push(lastPiece.sort());
            lastPiece = [char];
        }
        lastType = nextType;
    }
    piecesArray.push(lastPiece.sort());
    return piecesArray.flat().join("");    
}

let result = sortPieces(string1) 
console.log(result === string2, result);


This could also be done by just adding each sorted piece directly to a results array which then removes the need to use .flat() at the end. One could also use:

result = result.concat(lastPiece.sort())

too, but I don't generally like the fact that .concat() makes a whole new result array every time you call it (which seems less efficient to me if doing it a bunch of times) so that's why I used this:

result.push(...lastPiece.sort())

instead since you can add one array onto the other this way without making a whole new copy of the accumulated array every time (though it may be making a copy of lastPiece each time). Since .flat() could be implemented in native code, it may be plenty efficient (as used in the first implementation).

Anyway, here's an implementation that uses result.push(...lastPiece.sort()); to accumulate results as you go:

const string1 = 'AYUKB17053UI903TBC';
const string2  = 'ABKUY01357IU039BCT';

function sortPieces(str) {
    const result = [];
    let lastPiece = [];
    let lastType;
    for (const char of str) {
        const nextType = (char >= "0" && char <= "9") ? "number" : "letter";
        if (nextType === lastType || !lastType) {
            // either same type as previous char or first char in string
            lastPiece.push(char);
        } else {
            // different type of char than previous char, start a new piece
            result.push(...lastPiece.sort());
            lastPiece = [char];
        }
        lastType = nextType;
    }
    result.push(...lastPiece.sort());
    return result.join("");    
}

let result = sortPieces(string1) 
console.log(result === string2, result);


FYI, in a little performance benchmarking, the first option here is faster in Firefox and the second option here is faster in Chrome. Apparently Chrome is more efficient with result.push(...lastPiece.sort()); or worse with .flat().

\$\endgroup\$
5
\$\begingroup\$

You can use RegEx to extract the sequence of alphabets or numbers. To match alphabets/number, use character class in Regular expression as below

/[A-Z]+|[0-9]+/

to match all occurrences, use g(global) flag.

After extracting sequences, use Array#reduce with Array#sort & Array#join to get the sorted string.

const str1 = 'AYUKB17053UI903TBC';
const str2 = 'ABKUY01357IU039BCT';

console.log(str1
  .match(/[A-Z]+|[0-9]+/g)
  .reduce((a, c) => a + [...c].sort().join(''), '')
    === str2
);

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.