Winter
Winter

Reputation: 2517

Javascript - How to compare and sort one array based on the order of second array?

If I have an array with a bunch of posts sorted any old how:

[
    {
        id: 1,
        name: "first parent post"
    },
    {
        id: 2,
        name: "second child of first parent post"
    },
    {
        id: 3,
        name: "second parent post"
    },
    {
        id: 4,
        name: "first child of first parent post"
    },
    {
        id: 5,
        name: "first child of second parent post"
    }
]

However, another array decides the structure of the first array based on the ids of the first array:

[
    {
        id: 1,
        parent: 0
    },
    {
        id: 4,
        parent: 1
    },
    {
        id: 2,
        parent: 1
    },
    {
        id: 3,
        parent: 0
    },
    {
        id: 5,
        parent: 3
    }
]

What would be the most efficient way to sort these so that the first array is sorted by the second array?

I would expect the resulting array to look something like this:

[
    {
        id: 1,
        name: "first parent post",
        indent: 0
    },
    {
        id: 4,
        name: "first child of first parent post",
        indent: 1
    },
    {
        id: 2,
        name: "second child of first parent post",
        indent: 1
    },
    {
        id: 3,
        name: "second parent post",
        indent: 0
    },
    {
        id: 5,
        name: "first child of second parent post",
        indent: 1
    }
]

Upvotes: 0

Views: 73

Answers (3)

Brian McCutchon
Brian McCutchon

Reputation: 8584

For the ordering, convert the first array into a Map, then pull the information from that into the first array.

function combineArrays(arr1, arr2) {
    let names = new Map(arr1.map(({id, name}) => [id, name]));
    return arr2.map(({id, parent}) => ({id: id, name: names.get(id), parent: parent}));
}

This will give you name, id, and parent in the order you want. Calculating indent is left as an exercise to the reader (or should probably be a different question).

Upvotes: -1

Nina Scholz
Nina Scholz

Reputation: 386550

You need to generate a tree with the dependencies first and then render the indent for all items of the orginal array.

This proposal works for unsorted data/relations as well.

function getDataWithIndent(data, relation) {
    var hash = Object.create(null),
        tree = function (data, root) {
            var r = [], o = {};
            data.forEach(function (a) {
                a.children = o[a.id] && o[a.id].children;
                o[a.id] = a;
                if (a.parent === root) {
                    r.push(a);
                } else {
                    o[a.parent] = o[a.parent] || {};
                    o[a.parent].children = o[a.parent].children || [];
                    o[a.parent].children.push(a);
                }
            });
            return r;
        }(relation, 0),
        result = [];

    data.forEach(function (a) {
        hash[a.id] = a;
    });
    tree.forEach(function iter(indent) {
        return function (a) {
            hash[a.id].indent = indent;
            result.push(hash[a.id]);
            Array.isArray(a.children) && a.children.forEach(iter(indent + 1));
        };
    }(0));
    return result;
}

var data = [{ id: 1, name: "first parent post" }, { id: 2, name: "second child of first parent post" }, { id: 3, name: "second parent post" }, { id: 4, name: "first child of first parent post" }, { id: 5, name: "first child of second parent post" }],
    relation = [{ id: 1, parent: 0 }, { id: 4, parent: 1 }, { id: 2, parent: 1 }, { id: 3, parent: 0 }, { id: 5, parent: 3 }],
    result = getDataWithIndent(data, relation);
  
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

maioman
maioman

Reputation: 18734

You can order your data array by index and then order it by the order array;

to get the indentation you'll have to track the parent's indentation upstream and add one:

var data  = [
    {
        id: 1,
        name: "first parent post"
    },
    {
        id: 2,
        name: "second child of first parent post"
    },
    {
        id: 3,
        name: "second parent post"
    },
    {
        id: 4,
        name: "first child of first parent post"
    },
    {
        id: 5,
        name: "first child of second parent post"
    }
]

var ord = [
    {
        id: 1,
        parent: 0
    },
    {
        id: 4,
        parent: 1
    },
    {
        id: 2,
        parent: 1
    },
    {
        id: 3,
        parent: 0
    },
    {
        id: 5,
        parent: 3
    }
    
]
// first you arrange the data element by index
dataByIndex = data.reduce((ac, x) => {
  ac[x.id] = x;
  return ac
},[])

// then you order them by the ord array
var res = ord.reduce((ac, x, i) => {
   var item = dataByIndex[x.id]
   item.indent = x.parent === 0 ? 0 : getParentIndentPlusOne(ac, x.parent)
   
   return [...ac, item]
  },  [] )
  
function getParentIndentPlusOne(ac, id){
  var i = ac.length
  while (--i > -1){
    if (id === ac[i].id) return ac[i].indent + 1 
  }
}
  

   
console.log(res)

Upvotes: 1

Related Questions