Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort an array by its relative position

Example object array:

[{
    id: 'a',
    beforeId: null
}, {
    id: 'b',
    beforeId: 'c'
}, {
    id: 'c',
    beforeId: 'a'
}, {
    id: 'd',
    beforeId: 'b'
}]

Output order: d-b-c-a; each element sorted relative to each other element based on its beforeId property.

I could make a temporary array and sort the above array. Is sorting possible with array.sort?

like image 523
Shyamal Parikh Avatar asked Feb 16 '18 09:02

Shyamal Parikh


People also ask

What is relative sorting?

Relative Sort Array. Easy. Given two arrays arr1 and arr2 , the elements of arr2 are distinct, and all elements in arr2 are also in arr1 . Sort the elements of arr1 such that the relative ordering of items in arr1 are the same as in arr2 .

How do you sort an array in a specific order?

Sorting an array of objects in javascript is simple enough using the default sort() function for all arrays: const arr = [ { name: "Nina" }, { name: "Andre" }, { name: "Graham" } ]; const sortedArr = arr.

How do you sort one array with respect to another array?

Method 1 (Using Sorting and Binary Search)Create a temporary array temp of size m and copy the contents of A1[] to it. Create another array visited[] and initialize all entries in it as false. visited[] is used to mark those elements in temp[] which are copied to A1[]. Initialize the output index ind as 0.

Can you use sort () on an array?

Just like numeric arrays, you can also sort string array using the sort function. When you pass the string array, the array is sorted in ascending alphabetical order.


3 Answers

This is a terribly inefficient and naïve algorithm, but it works:

const array = [
    {id: 'a', beforeId: null},
    {id: 'b', beforeId: 'c'},
    {id: 'c', beforeId: 'a'},
    {id: 'd', beforeId: 'b'}
];

// find the last element
const result = [array.find(i => i.beforeId === null)];

while (result.length < array.length) {
    // find the element before the first element and prepend it
    result.unshift(array.find(i => i.beforeId == result[0].id));
}

console.log(result);
like image 21
deceze Avatar answered Oct 01 '22 01:10

deceze


Is sorting possible with array.sort?

sure, with a helper function:

graph = [
    {id: 'a', beforeId: null},
    {id: 'b', beforeId: 'c'},
    {id: 'c', beforeId: 'a'},
    {id: 'd', beforeId: 'b'}
];

let isBefore = (x, y) => {
    for (let {id, beforeId} of graph) {
        if (id === x)
            return (beforeId === y) || isBefore(beforeId, y);
    }
    return false;
};

graph.sort((x, y) => x === y ? 0 : (isBefore(x.id, y.id) ? -1 : +1))

console.log(graph);

isBefore returns true if x is before y immediately or transitively.

For generic, non-linear topological sorting see https://en.wikipedia.org/wiki/Topological_sorting#Algorithms

UPD: As seen here, this turned out to be horribly inefficient, because sort involves many unnecessary comparisons. Here's the fastest (so far) version:

function sort(array) {
    let o = {}, res = [], len = array.length;

    for (let i = 0; i < len; i++)
        o[array[i].beforeId] = array[i];

    for (let i = len - 1, p = null; i >= 0; i--) {
        res[i] = o[p];
        p = o[p].id;
    }

    return res;
}

which is the @Nina's idea, optimized for speed.

like image 43
georg Avatar answered Sep 30 '22 23:09

georg


You could build an object with the relations and generate the result by using the object with beforeId: null and unshift all objects for the result array.

The next object is the one with the actual val as key.

Complexity: O(2n).

function chain(array) {
    var o = {}, pointer = null, result = [];

    array.forEach(a => o[a.beforeId] = a);

    while (o[pointer]) {
        result.unshift(o[pointer]);
        pointer = o[pointer].val;
    }

    return result;
}

var data = [{ val: 'a', beforeId: null }, { val: 'b', beforeId: 'c' }, { val: 'c', beforeId: 'a' }, { val: 'd', beforeId: 'b' }];

console.log(chain(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
like image 106
Nina Scholz Avatar answered Sep 30 '22 23:09

Nina Scholz