Home > Enterprise >  Custom sorting 2D array
Custom sorting 2D array

Time:09-29

I have a 2D array whose entries represent an employee including the name of their manager, sort of like this:

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

If the employee and the manager are the same, it means that the person is highest up in the hierarchy. What I want to achieve is to sort each employee below their manager, alphabetically. In other words, the output I want is this:

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

My attempt was to use array.sort(compare) with the following "compare" function:

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));

Unfortunately, what I get as output is just an array sorted alphabetically by the name of the employee. Plus, the "header" is included in the sort as well, which I want not to happen.

What am I doing wrong, any tips or alternative suggestions other than using Array.prototype.sort()?

Please, kind soul, help me!

CodePudding user response:

You need a topological sorting and get the itmes as flat array from a tree.

const
    sort = array => {
        const
            getNames = manager => employee => [
                [employee, manager || employee],
                ...(t[employee] || [])
                    .sort(([a], [b]) => a.localeCompare(b))
                    .flatMap(getNames(employee))
            ],
            root = [],
            t = {};

        array.forEach(([employee, manager]) => {            
            if (employee === manager) manager = '';
            (t[manager] ??= []).push(employee);
        });

        return t[''].flatMap(getNames(''));
    },
    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; }

CodePudding user response:

I don't fully understand your goal because your expected results and your explaination don't line up (but it's probably just me not seeing it). But here's something I wrote that I feel is meeting your criteria?

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));

It's result is as follows which is different to your expected results but meet the criteria:

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

CodePudding user response:

The sort you have described is basically a flattened tree structure.

Here building the tree by reference in a Map, pushing top-level elements (employee === manager) to the tree, then flattening using a queue.

I expanded the dataset to include more properties as an example based on your comment on Nina's answer.

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; }

  • Related