# 937. Reorder Data in Log Files

## Problem:

Reorder the logs based on the following rules:

1. The identifier, which is the first part of the log, doesn't impact the order.
2. Letter-logs should be ordered before those digit-logs.
3. When the content of the word logs is identical, the logs should then be sorted based on their identifier.
4. Digit-logs should remain in the original input order.

Note: If their contents are the same, then sort them lexicographically by their identifiers.

{% code overflow="wrap" %}

```
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"]
Explanation: The letter-log contents are all different, so their ordering is "art can", "art zero", "own kit dig". The digit-logs have a relative order of "dig1 8 1 5 1", "dig2 3 6".
```

{% endcode %}

<https://leetcode.com/problems/reorder-data-in-log-files/>

## What to learn:

* `set` doesn't preserve order of insertion, but a `list` does.

* The loop for tuples in the following solution is better than the one in the first solution:<br>

  ```python
  for _, _, i in new_arr:  
      result_arr.append(logs[i])
  ```

* Python's `sort` function allows for complex sorting rules by returning **tuples** from the key function.\
  \
  If the key function -- in the last solution, `lambda` -- returns a tuple, then `sort` will sort by the elements of the tuple in order - first sorts all elements primarily according to the first element of the tuple. This is sometimes referred to as "lexicographical" or "dictionary" order. In other words, this code is sorting the strings in `letters` first by the second word in each string, and then (in the case of ties) by the first word in each string.

## Solutions:

1. Divide the provided list into digits and letters.
2. Sort the letters excepting the identifier.
3. Return the entire list.

<pre class="language-python"><code class="lang-python">class Solution:
    def reorderLogFiles(self, logs: List[str]) -> List[str]:
        ordered_set = set()
        num_list = []
        new_arr = []
        result_arr = []
        # Separate letter-logs and digit-logs
        # Save indices
        arr = [item.split() for item in logs]
        for idx, elem in enumerate(arr):
            # if the log contains a digit, it's a digit-log
            if elem[1].isdigit():
                num_list.append(idx)
            # otherwise, it's a letter-log
            else:
                ordered_set.add(idx)
        # tuple: (identifier, log_content, index)
        for i in ordered_set:
            log_parts = logs[i].split(" ", 1)  # split only on the first space
            identifier = log_parts[0]
            content = log_parts[1]
            new_arr.append((identifier, content, i))

        # Sort by identifier and log content
        new_arr.sort(key=lambda x: (x[1], x[0]))  # sort primarily by content, then by identifier

        # Letter-logs ordering
<strong>        for tup in new_arr:
</strong>            result_arr.append(logs[tup[2]])

        # Digit-logs ordering
        for i in num_list:
            result_arr.append(logs[i])
        return result_arr
</code></pre>

### Better approach

<pre class="language-python"><code class="lang-python">class Solution:
    def reorderLogFiles(self, logs: List[str]) -> List[str]:
        letters, digits = [], []
        for log in logs:
            if log.split()[1].isdigit():
                digits.append(log)
            else:
                letters.append(log)
<strong>        letters.sort(key=lambda x: x.split()[1:], x.split()[0]))
</strong>        return letters + digits
</code></pre>

<details>

<summary>Here's how it works:</summary>

```python
# s = ['2 A', '1 B', '4 C', '1 A']
def func(x):
    return x.split()[1], x.split()[0]
sorted(s)        # ['1 A', '1 B', '2 A', '4 C']
s.sort(key=func) # ['1 A', '2 A', '1 B', '4 C']
```

1. `func` is a function that takes a string `x` as input. This string is expected to contain words separated by spaces.
2. `x.split()[1]` and `x.split()[0]` are used to split the input string `x` into a list of words. `[1]` and `[0]` are the indices used to access the second word and first word in the list respectively.
3. `func` returns a tuple of two elements: the second word in the string, and then the first word in the string.
4. `s.sort(key=func)` sorts the list `s` in place. The `key` parameter is used to specify a function of one argument that is used to extract a comparison key from each element in `s`. In this case, it's using `func` as the key, meaning it's sorting the strings primarily by their second word, and then by their first word.

In other words, this code is sorting the strings in `s` first by the second word in each string, and then (in the case of ties) by the first word in each string.

If `func` returned a tuple with more than two elements, `sort` would continue this process, comparing the next element in the tuple **only if all the previous elements were equal.** This lets you sort by as many criteria as you want, simply by extending the tuple returned by your key function.

</details>
