Пользовательская сортировка 2D массива

#javascript #arrays #sorting

Вопрос:

У меня есть 2D массив, записи в котором представляют сотрудника, включая имя его менеджера, примерно так:

 var array = [["Name", "Manager"],
["Leonard", "Penny"],
["Penny", "Professor Proton"],
["Sheldon", "Bernadette"],
["Raj", "Penny"],
["Professor Proton", "Professor Proton"],
["Howard", "Bernadette"],
["Bernadette", "Professor Proton"]]
 

Если сотрудник и менеджер-одно и то же, это означает, что этот человек занимает самое высокое место в иерархии. Чего я хочу добиться, так это отсортировать каждого сотрудника ниже его руководителя в алфавитном порядке. Другими словами, результат, который я хочу, это:

 [["Name", "Manager"],
["Professor Proton", "Professor Proton"],
["Bernadette", "Professor Proton"],
["Howard", "Bernadette"],
["Sheldon", "Bernadette"],
["Penny", "Professor Proton"],
["Leonard", "Penny"],    
["Raj", "Penny"]]
 

Моя попытка состояла в том, чтобы использовать array.sort(compare) следующую функцию «сравнить» :

 var array = [
  ["Name", "Manager"],
  ["Leonard", "Penny"],
  ["Penny", "Professor Proton"],
  ["Sheldon", "Bernadette"],
  ["Raj", "Penny"],
  ["Professor Proton", "Professor Proton"],
  ["Howard", "Bernadette"],
  ["Bernadette", "Professor Proton"]
];

function compare(a, b) {

  if (a[0] === a[1]) {
    return -1;
  }

  if (b[0] === b[1]) {
    return 1;
  }

  if (a[0] === b[1]) {
    return -1;
  }

  if (b[0] === a[1]) {
    return 1;
  }


  if (a[0].toLowerCase() < b[0].toLowerCase()) {
    return -1;
  }

  if (a[0].toLowerCase() > b[0].toLowerCase()) {
    return 1;
  }

  return 0;
}

console.log(array.sort(compare)); 

К сожалению, то, что я получаю в качестве вывода, — это просто массив, отсортированный в алфавитном порядке по имени сотрудника. Кроме того, «заголовок» также включен в сортировку, чего я не хочу, чтобы произошло.

Что я делаю не так, какие-либо советы или альтернативные предложения, кроме использования Array.prototype.sort() ?

Пожалуйста, добрая душа, помоги мне!

Комментарии:

1. Просто, вы можете заставить фрагмент работать ?

2. Привет, спасибо за ваш комментарий и извинения, я не часто использовал функцию сниппета в прошлом. Я внес в него изменения, и вы должны быть в состоянии запустить его сейчас и увидеть результат 🙂

3. нет проблем x) спасибо ! это прекрасно

4. Просто я не понимаю, почему «Профессор Протон» идет за «Бернадетт» (в требуемом выводе), можете ли вы объяснить ?

5. Эй, вообще-то, профессор Протон должен прийти раньше Бернадетт, потому что он ее «менеджер». Фактически, профессор Протон должен быть в верхней части списка (потому что он единственный, кто является «его собственным менеджером» (в списке всегда будет только один уникальный человек, удовлетворяющий этому условию).

Ответ №1:

Вам нужно дерево и получить элементы в глубину-первый обход. Этот (новый подход) работает без изменения полезной нагрузки.

 const
    sort = array => {
        const
            t = {},
            getManager = data => data[0] === data[1] ? '' : data[1],
            getData = manager => (t[manager] || [])
                .sort(([a], [b]) => a.localeCompare(b))
                .flatMap(data => [data, ...getData(data[0])]);

        array.forEach(data => (t[getManager(data)] ??= []).push(data));

        return getData('');
    },
    data = [["Name", "Manager"], ["Leonard", "Penny"], ["Penny", "Professor Proton"], ["Sheldon", "Bernadette"], ["Raj", "Penny"], ["Professor Proton", "Professor Proton"], ["Howard", "Bernadette"], ["Bernadette", "Professor Proton"]],
    result = [data[0], ...sort(data.slice(1))];
    
console.log(result); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Комментарии:

1. Вау, кажется, это работает безупречно! Большое тебе спасибо, Нина! Я действительно понятия не имею, как и почему это работает :D, Следовательно, я даже не могу приспособить это к моей конкретной ситуации (проблема из моего вопроса была упрощена, на самом деле у каждого сотрудника есть больше информации, чем просто их имя и имя их менеджера, например, смена, язык и т. Д.), Но я думаю, что потрачу следующие пару часов на выяснение этого 🙂 Ура!

2. @finaslonchas, извини. пожалуйста, смотрите раздел правка. теперь он принимает данные без их изменения.

Ответ №2:

Тип, который вы описали, в основном представляет собой уплощенную древовидную структуру.

Здесь построение дерева по ссылке в a Map , перемещение элементов верхнего уровня ( employee === manager ) в дерево, затем выравнивание с помощью очереди.

Я расширил набор данных, чтобы включить больше свойств в качестве примера, основанного на вашем комментарии к ответу Нины.

 const input = [
  ['Name', 'Manager', 'Shift', 'Language'],
  ['Leonard', 'Penny', 'Day', 'en'],
  ['Penny', 'Professor Proton', 'Day', 'en'],
  ['Sheldon', 'Bernadette', 'Night', 'en'],
  ['Raj', 'Penny', 'Night', 'en'],
  ['Professor Proton', 'Professor Proton', 'Swing', 'en'],
  ['Howard', 'Bernadette', 'Night', 'en'],
  ['Bernadette', 'Professor Proton', 'Day', 'en'],
];

const [head, ...temp] = input;

const map = new Map(temp
  .sort((a, b) => a[0].localeCompare(b[0]))
  .map(([, m]) => [m, []]));
  
const tree = [];
for (const [n, m, ...rest] of temp) {
  const o = { employee: [n, m, ...rest], children: map.get(n) };
  if (n === m) {
    tree.push(o);
  } else {
    map.get(m).push(o);
  }
}

const res = [head];
while (tree.length) {
  const { employee, children } = tree.shift();
  res.push(employee);
  tree.unshift(...(children ?? []));
}

console.log(res); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Ответ №3:

Я не до конца понимаю вашу цель, потому что ваши ожидаемые результаты и ваши объяснения не совпадают (но, вероятно, я просто этого не вижу). Но вот что я написал, что, по моему мнению, соответствует вашим критериям?

 const input = [
  ['Name', 'Manager'],
  ['Leonard', 'Penny'],
  ['Penny', 'Professor Proton'],
  ['Sheldon', 'Bernadette'],
  ['Raj', 'Penny'],
  ['Professor Proton', 'Professor Proton'],
  ['Howard', 'Bernadette'],
  ['Bernadette', 'Professor Proton'],
];

const sort = (arr) => {
  return arr.map(el => [el[1], el[0]]) //swap employee and manager
            .sort() //sort by the first element (i.e. manager)
            .map(el => [el[1], el[0]]) //swap back employee and manager
            .sort((a,b) => b[0] === b[1] ? 1 : -1 ); //move element to front if employee is the manager
}

console.log(sort(input));
 

Его результат выглядит следующим образом, который отличается от ваших ожидаемых результатов, но соответствует критериям:

 [["Professor Proton", "Professor Proton"],
["Penny", "Professor Proton"],
["Bernadette", "Professor Proton"],
["Raj", "Penny"],
["Leonard", "Penny"],
["Name", "Manager"],
["Sheldon", "Bernadette"],
["Howard", "Bernadette"]]
 

Комментарии:

1. Эй, спасибо за ваш ответ! В принципе, у меня есть три уровня в иерархии: уровень 1 = профессор Протон, уровень 2 = Бернадетт и Пенни (они подчиняются профессору Протону), уровень 3 = Говард, Леонард, Радж, Шелдон (они подчиняются либо Бернадетт, либо Пенни). Я хочу, чтобы профессор Протон был наверху, а затем ниже Бернадетт, затем Говарда и Шелдона (в алфавитном порядке). Затем я хочу, чтобы появилась Пенни (она идет в алфавитном порядке после Бернадетт, и они находятся на одном уровне), затем Леонард и Радж в алфавитном порядке. Извините, если я был неясен в своем вопросе (был на моем втором косяке).