0

I'm trying to solve the Reorder Data in Log Files algorithm.

You have an array of logs. Each log is a space delimited string of words.

For each log, the first word in each log is an alphanumeric identifier. Then, either:

Each word after the identifier will consist only of lowercase letters, or; Each word after the identifier will consist only of digits. We will call these two varieties of logs letter-logs and digit-logs. It is guaranteed that each log has at least one word after its identifier.

Reorder the logs so that all of the letter-logs come before any digit-log. The letter-logs are ordered lexicographically ignoring identifier, with the identifier used in case of ties. The digit-logs should be put in their original order.

Return the final order of the logs.

Example:

Input: logs = ["dig1 8 1 5 1","let1 art can","dig2 3 6","let2 own kit dig","let3 art zero"]
Output: ["let1 art can","let3 art zero","let2 own kit dig","dig1 8 1 5 1","dig2 3 6"]

My idea is having a map for the digits and one for the letters. I have done it. Then, I would need to sort the digits and letters and add all the sorted letters to my answer array and all the sorted digits to my answer array.

var reorderLogFiles = function(logs) {
    if(!logs || logs.length === 0)
        return [];
    
    let numbers = {
        '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6,
        '7': 7, '8': 8, '9': 9
    };
    let digits = new Map();
    let letters = new Map();
    
    for(let i=0; i<logs.length; i++) {
        const log = logs[i].split(" ");
        if(numbers[log[1]] !== undefined) 
            digits.set(log[0], log.splice(1, log.length));
        else
            letters.set(log[0], log.splice(1, log.length));
    }
    
    // How can I sort letter and digits?
    
    let ans = [];
    for(const [key, value] of sortedLetters) {
        const temp = key + " " + value.join(" ");
        ans.push(temp);
    }
    for(const [key, value] of sortedDigits) {
        const temp = key + " " + value.join(" ");
        ans.push(temp);
    }
    
    return ans;
};
myTest532 myTest532
  • 2,091
  • 3
  • 35
  • 78

1 Answers1

1

I think you can simplify your code somewhat. First, create the digits and letters groups by filtering the original logs; this can be made easier by first splitting all the values in logs. Next, sort the letters based on the second value in the array and add the digits to the end of the sorted array. Finally, join the strings back together:

const reorderLogFiles = logs => {
  // split values on first space
  logs = logs.map(v => v.split(/\s+(.*)/).filter(Boolean));
  // filter into digits and letters
  let digits = logs.filter(v => v[1].match(/^[\s\d]+$/));
  let letters = logs.filter(v => v[1].match(/^[a-z\s]+$/));
  // sort the letters
  letters.sort((a, b) => (c = a[1].localeCompare(b[1])) ? c : a[0].localeCompare(b[0]));
  // reassemble the list
  result = letters.concat(digits);
  // and convert back to strings
  result = result.map(a => a.join(' '));
  return result;
}

let logs = ["dig1 8 1 5 1", "let1 art can", "dig2 3 6", "let2 own kit dig", "let3 art zero"];
console.log(reorderLogFiles(logs));
logs = ["a1 9 2 3 1", "g1 act car", "zo4 4 7", "ab1 off key dog", "a8 act zoo", "a2 act car"];
console.log(reorderLogFiles(logs));

Note this code can be written more compactly by chaining operations but I've written it out more fully to make it easier to follow.

If you don't want to use regex, you can test the first character of each substring to see if it's a digit or letter. For example:

let digits = logs.filter(v => v[1][0] >= '0' && v[1][0] <= '9');
let letters = logs.filter(v => v[1][0] >= 'a' && v[1][0] <= 'z');
Nick
  • 138,499
  • 22
  • 57
  • 95
  • Thank you; however, it fails for the following input: ["a1 9 2 3 1","g1 act car","zo4 4 7","ab1 off key dog","a8 act zoo","a2 act car"]. The output should be: ["a2 act car","g1 act car","a8 act zoo","ab1 off key dog","a1 9 2 3 1","zo4 4 7"] – myTest532 myTest532 Aug 04 '20 at 14:42
  • "The letter-logs are ordered lexicographically ignoring identifier, with the identifier used in case of ties" I think this is the issue – myTest532 myTest532 Aug 04 '20 at 15:00
  • @myTest532myTest532 - sorry, I missed that point. I've updated the answer. – Nick Aug 04 '20 at 22:39
  • Thank you. Is there a way to do it without using regex? – myTest532 myTest532 Aug 05 '20 at 17:00
  • @myTest532myTest532 sure, see my edit, or see https://stackoverflow.com/questions/8935632/check-if-character-is-number and https://stackoverflow.com/questions/9862761/how-to-check-if-character-is-a-letter-in-javascript for other possibilities – Nick Aug 05 '20 at 23:12
  • Thank you! Good solution – myTest532 myTest532 Aug 06 '20 at 15:23