3

I have an array of javascript arrays, with each inner array being of equal length. I want to sort one of the inner arrays chronologically (its members are all numbers), and I want the other arrays to re-order in the same way. E.g.

we start with this:

[[2, 3, 1], ["deux", "trois", "un"], ["zwei", "drei", "eins"]]

and the result I want is:

[[1, 2, 3], ["un", "deux", "trois"], ["eins", "zwei", "drei"]]

I've tried different variations on the following, but had no luck:

arr.sort((a, b) => a[0] - b[0])

All I get back is exactly what I put in!

2
  • is each of a sub arrays's item unique (at least within its containing array)? Commented Aug 17, 2020 at 13:03
  • Good question, no there will likely be multiple instances of the same value in each sub array. Commented Aug 17, 2020 at 13:08

4 Answers 4

2

You could get an array of sorted indices and map sorted arrays.

const
    arrays = [[2, 3, 1], ["deux", "trois", "un"], ["zwei", "drei", "eins"]],
    sorted = arrays.map(
        (indices => a => indices.map(i => a[i]))
        ([...arrays[0].keys()].sort((a, b) => arrays[0][a] - arrays[0][b]))
    );

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

A function for it.

const
    sortNumbers = (a, b) => a - b,
    sortABC = (a, b) => a.localeCompare(b),
    sortArray = (arrays, index, fn) => {
        const
            source = arrays[index],
            indices = [...source.keys()].sort((a, b) => fn(source[a], source[b]));
        return arrays.map(a => indices.map(i => a[i]));
    },
    arrays = [[2, 3, 1], ["deux", "trois", "un"], ["zwei", "drei", "eins"]],
    sorted0 = sortArray(arrays, 0, sortNumbers),
    sorted1 = sortArray(arrays, 1, sortABC);
    sorted2 = sortArray(arrays, 2, sortABC);

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

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

6 Comments

Am I wrong to think this does a lot of extra work every time around?
no just once for the closure over the indices and then it returns new mapped arrays.
Thanks! This doesn't appear to work for longer length sub-arrays though. Why could that be?
@Will, it should work for any length. it had a littel mistake by taking the arrays indices instead of the nested array.
@PeterSeliger, beside the index, the function takes now a function for comparing two values. anway. is it really important?
|
2

This should do the trick, we should get the new indexes for array of numbers then apply the same new indexes to non number arrays:

const arrays = [
  [2, 3, 1],
  ["deux", "trois", "un"],
  ["zwei", "drei", "eins"],
];
const [arrNbrs] = arrays;

const newIndexes = [...arrNbrs].sort().map((nbr) => arrNbrs.indexOf(nbr));

const sortedArrays = arrays.map((subArray) =>
  subArray.map((_, index) => subArray[newIndexes[index]])
);

console.log("sortedArrays", sortedArrays);

2 Comments

such terse, very one-liner ;)
the power of FP pipelines, they swallow other lines :p
1

const test = [
  [3, 2, 1, 3, 2, 1, 2],
  ["trois", "deux", "un", "trois", "deux", "un", "deux"],
  ["drei", "zwei", "eins", "drei", "zwei", "eins", "zwei"]
];


function sortListsAccordingToReferenceList(
  listOfLists,
  listIndex,
  referenceComparator
) {
  function defaultComparator(a, b) {
    return (
      ((a.localeCompare) && (b.localeCompare))
        ? a.localeCompare(b)
        : (((a < b) && -1) || ((a > b) && 1) || 0)
    );
  }
  const comparator = (typeof referenceComparator === 'function')
    ? referenceComparator
    : defaultComparator;

  function compareReferenceItems(a, b) {
    return comparator(a.value, b.value);
  }
  const referenceList = listOfLists[listIndex];
  const lastIndexList = referenceList.map((item, idx) => ({

    lastIndex: idx,
    value: item

  })).sort(compareReferenceItems).map(referenceItem =>

    referenceItem.lastIndex
  );
  // console.log('lastIndexList : ', lastIndexList);

  // - in case of needing to return the
  //   mutated original array use this block ...
  // 
  // listOfLists.forEach((list, idx) => {
  // 
  //   const duplicates = Array.from(list);
  //   lastIndexList.forEach((lastIndex, idx) => {
  // 
  //     list[idx] = duplicates[lastIndex];
  //   });
  // });
  // return listOfLists; // return mutated original array.

  // - ... otherwise go with pure mappping.
  return listOfLists.map(list =>
    list.map((item, idx) => list[lastIndexList[idx]])
  ); 
}

console.log(
  'sort every array according to numeric sort order ...',
  sortListsAccordingToReferenceList(test, 0)
);
console.log(
  'sort everything by French as lexicographic sort reference ...',
  sortListsAccordingToReferenceList(test, 1)
);
console.log(
  'sort everything by German as lexicographic sort reference ...',
  sortListsAccordingToReferenceList(test, 2)
);

console.log(
  'sort every array according to numeric custom sort order ...',
  sortListsAccordingToReferenceList(test, 0, (a, b) =>
    // comparator function for descending sort order
    (((a > b) && -1) || ((a < b) && 1) || 0)
  )
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Comments

0

Not a oneliner, but easier to read

let nested = [
  [2, 3, 1],
  ["deux", "trois", "un"],
  ["zwei", "drei", "eins"]
];
let srcArr;
nested = nested.map((arr, i) => {
  if (i === 0) { // the reference
    srcArr = arr.slice(0); // take a copy
    arr.sort((a, b) => a - b); // sort the nested one
    return arr;
  }
  return arr.map((item, i) => arr[
    srcArr.indexOf(nested[0][i]) // return in the order of the reference 
  ]);
})
console.log(nested)

Comments

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.