2

I've done something similar in the past with a nested loop, but for some reason I can't get my mind around this to get it to work. I keep running into problems that involve indexing of two separate arrays and it's been a continual stumbling block

In this case, I'm trying to sort a string. The string includes letters and numbers, the task is to sort the letters in reverse alphabetical order while keeping the numbers at their same index.

I've come up with this solution (probably not the most efficient), but can't get the sortString array to come together so that I can join the letters and numbers back into a string.

function reverse(str) {
    // split the str into an array
  const arr = [...str]
  // converts each element in arr to a number, letters are string 'NaN'
  const numArray = arr.map(x=> Number(x)).map(x=> x >= 0 ? x : String(x))
  // array of the sorted letters
  const letters = arr.filter(x=> !/[0-9]/g.test(x)).reverse()
    // initiate empty array to hold the combined numbers and letters
  let sortString = []
  // Use for loop to cycle through and replace elements that are 'NaN' with letters from the letter array.  All pushed to sortString.
  for (let i=0; i<arr.length; i++) {
    sortString.push(numArray[i] === 'NaN' ? letters[0] : numArray[i])
  }
 return sortString
}


reverse("ab89c") // output should be "cb89a"
  • Could someone could help with the last part of the solution - if the `numArray` element is `'NaN'` (note it's converted to a string to act as a placeholder), add the corresponding element from the `letters` array, if not, keep `numArray` in its same index in `sortString`? I tried using a for loop nested in another but couldn't figure out how to get it to work...I'm trying to coordinate the indexing if that makes sense. ``` – Nick Huemmer Jul 14 '22 at 17:54

2 Answers2

1

You could get an array of non digits, sort it and map the splitted string with the sorted letters in places if not a digit.

const
    reverse = string => {
        const
            array = Array.from(string),
            letters = array
                .filter(v => /\D/.test(v))
                .sort();
            
        return array
            .map(v => /\D/.test(v) ? letters.pop() : v)
            .join('');
    };

console.log(reverse("ab89c"));

A slightly different approach takes a Proxy for the wanted items of sorting:

How to sort only part of array? between given indexes

Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • If I understand correctly, when letters is initialized, it's sorted ascending, but when the lambda is returning, it pops from letters, and pop does last element first, therefore sorting backwards, as desired??? – Jesus is Lord Jul 14 '22 at 16:07
  • Elegant solution - it's a different approach than I took but appears to be more efficient (i.e. there are less steps involved). Thanks for the help! – Nick Huemmer Jul 14 '22 at 17:50
1

Here's code that works:

Explanation of how the code works is in-line as comments.

Basically it takes the numbers out, sorts the letters in reverse, and puts the sorted letters back in the right place.

Because it's step-by-step, you could add console log on each variable after it's assigned to see how it works step by step.

function reverse(input) {
    // This function from: https://stackoverflow.com/a/32567789/569302
    function testIsLetter(c) {
        return c.toLowerCase() != c.toUpperCase();
    }
    // Convert from array to string to process character by character
    let inputAsArray = input.split('');
    // This is where we'll lookup where to put the letters when we're done
    let mapped = inputAsArray.map((s) => {
        return {
            s,
            isLetter: testIsLetter(s)
        }
    })
    // Now that we've captured where the letters are, take the numbers (non-letters) out
    let filtered = mapped.filter(m => m.isLetter)
    // Convert the object into just letters so they're easily compared when we sort
    let filteredAsLettersArray = filtered.map(f => f.s)
    // Sort ascending
    filteredAsLettersArray.sort()
    // Reverse to sort descending
    filteredAsLettersArray.reverse()
    // Now we need to put the letters back.
    let resultAsArray = [];
    let letterIndex = 0;
    mapped.forEach(m => {
        // If it's a letter, we use the sorted result (incrementing letterIndex each time)
        if (m.isLetter) {
            resultAsArray.push(filteredAsLettersArray[letterIndex]);
            letterIndex++;
        } else {
            // Otherwise we use the number
            resultAsArray.push(m.s);
        }
    });
    let result = resultAsArray.join('');

    return result;
}
console.log(reverse("ab89c"));
console.log(reverse("1a2eb8f9c"));
Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97
  • 1
    Wow, you cranked this out. Interesting approach - it's very similar to how I initially framed it up mentally but couldn't put together. Thanks for the steps with explanations. – Nick Huemmer Jul 14 '22 at 18:02