0

I'm wanting to sort an array of arrays based on a array and then by length of items in the array.

Take the following master sort array:

const key = [
  "meraki",
  "gsuite",
  "active directory",
  "sophos",
  "manageengine"
]

I want to take a array that resembles:

const raw = [
  ["manageengine"],
  ["sophos"],
  ["active directory"],
  ["gsuite"],
  ["meraki"],
  ["sophos", "manageengine"],
  ["active directory", "sophos"],
  ["active directory", "manageengine"],
  ["gsuite", "active directory"],
  ["gsuite", "sophos"],
  ["gsuite", "manageengine"],
  ["meraki", "gsuite"],
  ["meraki", "active directory"],
  ["meraki", "sophos"],
  ["meraki", "manageengine"],
  ["active directory", "sophos", "manageengine"],
  ["gsuite", "active directory", "sophos"],
  ["gsuite", "active directory", "manageengine"],
  ["gsuite", "sophos", "manageengine"],
  ["meraki", "gsuite", "active directory"],
  ["meraki", "gsuite", "sophos"],
  ["meraki", "active directory", "sophos"],
  ["meraki", "gsuite", "manageengine"],
  ["meraki", "active directory", "manageengine"],
  ["meraki", "sophos", "manageengine"],
  ["gsuite", "active directory", "sophos", "manageengine"],
  ["meraki", "gsuite", "active directory", "sophos"],
  ["meraki", "gsuite", "active directory", "manageengine"],
  ["meraki", "gsuite", "sophos", "manageengine"],
  ["meraki", "active directory", "sophos", "manageengine"],
  ["meraki", "gsuite", "active directory", "sophos", "manageengine"]
];

In the above example, I want the raw array to be sorted by accordingly to each item in the key array. My first attempt was to do something like:

const result = [];
for (const name of result) {
  const sorted = keys.filter((s) => s[0] === name);
  result.push(...sorted);
}

result.sort((a, b) => a.length - b.length);

However that only takes into account the first item in the array, not the sort of the rest of the items.

4
  • 1
    Is the key constant, or does it change with some kind of input or expected code changes? Commented Oct 20, 2020 at 15:59
  • 1
    I think you want for (const name of raw), then, in that loop, const sorted = name.sort((a,b) => keys.indexOf(a) - keys.indexOf(b)). You'll likely need some kind of recursive sort for the outer array, since it needs to sort by length, then by the contents of each array. Commented Oct 20, 2020 at 16:09
  • 2
    Firstly, your "result" contains different items from the "raw" array - the third line, ["sophos", "active directory"] seems to have become ["meraki", "active directory"]. Is that just a typo? Secondly, your requirement was to sort by key then length but your output is by length then key - which is it? Should all those starting with "meraki" appear first and sorted by length, or should all those with just one entry appear first, sorted by key? Commented Oct 20, 2020 at 16:51
  • @ATD - Good catch - i updated the data. Commented Oct 20, 2020 at 18:00

2 Answers 2

1

To sort you have to first check the length. If both are equal we have to check the index positions of the first element of a/b within key. If those are the same move on to the next element in both arrays.

This answer makes use of the fact that 0 is a falsey value. Examples are: 0 || -1 //=> -1 and 1 || -1 //=> 1

const key = ["meraki", "active directory", "sophos"];

const raw = [
  ["meraki"],
  ["active directory"],
  ["sophos", "active directory"],
  ["active directory", "sophos"],
  ["sophos"],
  ["meraki", "active directory", "sophos"],
];

raw.sort((a, b) => (
  a.length - b.length || a.reduce((diff, _, i) => (
    diff || key.indexOf(a[i]) - key.indexOf(b[i])
  ), 0)
));

console.log(raw);
console.table(raw); // check browser console

Sign up to request clarification or add additional context in comments.

1 Comment

Another option would be a.reduce((diff, a, i) => diff || key.indexOf(a) - key.indexOf(b[i]), 0) instead of upto(a.length).reduce(...) but I despise the asymmetry in that solution.
1

Consider below approach

const key = [
  "meraki",
  "active directory",
  "sophos"
]
const raw = [
   ["sophos"],
   ["meraki"],
   ["active directory"],
   ["sophos", "active directory"],
   ["active directory", "sophos"],
   ["meraki", "active directory", "sophos"]
]

const compareThis = (a, b) => {
  if (a.length !== b.length) {
    return a.length - b.length
  }
  let itemFound = 0;
  for (let keyIndex in key) {
    for (let aIndex in a ) {
      if(a[aIndex] === key[keyIndex]) {
        itemFound = -1;
        break;
      }
      if(b[aIndex] === key[keyIndex]) {
        itemFound = 1;
        break;
      }
    }
    if(itemFound !== 0) { break }
  }
  return itemFound;
}

const sortedData = raw.sort(compareThis)

console.log(sortedData)

3 Comments

Side note: you are missing the 'var' in the 'for' loops
I think its inverted now.
Could you share a wider array for debugging?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.