按父级排序(拓扑排序)和数组上元素的重要性

时间:2019-01-18 16:25:01

标签: javascript arrays sorting lodash topological-sort

好吧,我有一个对象数组,其中某些元素取决于其他元素。

因此,我需要按重要性(父级依赖性)对其进行排序,以将其存储在数据库中,并用相应的父级parent替换所有子级id的属性。

数组示例:

[
    {
        "id": 1,
        "email": "a@b.com", // unique
        "parent": "c@b.com" // is nullable
    },
    {
        "id": 2,
        "email": "b@b.com",
        "parent": null
    },
    {
        "id": 3,
        "email": "c@b.com",
        "parent": "b@b.com"
    },
    {
        "id": 4,
        "email": "d@b.com",
        "parent": "a@b.com"
    },
    ...
]

依赖关系的图形示例:

enter image description here

预期结果: 按依赖关系(父项)排序:

[
    {
        "id": 2,
        "email": "b@b.com",
        "parent": null
    },
    {
        "id": 3,
        "email": "c@b.com",
        "parent": 2
    },
    {
        "id": 1,
        "email": "a@b.com",
        "parent": 3 
    },
    {
        "id": 4,
        "email": "d@b.com",
        "parent": 1
    },
    ...
]

要设置我正在使用的各个父级id(但按父级排序:父级,子级,子级...):

let users = [
{
    "id": 1,
    "email": "a@b.com", // unique
    "parent": "c@b.com" // is nullable
},
{
    "id": 2,
    "email": "b@b.com",
    "parent": null
},
{
    "id": 3,
    "email": "c@b.com",
    "parent": "b@b.com"
},
{
    "id": 4,
    "email": "d@b.com",
    "parent": "a@b.com"
}
];

users = users.map(user => {
    user.parent = _.findIndex(users, i => user.parent === i.email);
    return user;
});

P.S: 在这种情况下,importance概念是指parent级别。 所以,首先我需要父母,然后是孩子,孙子等等……

很抱歉,如果该主题的解释不充分,如果您有疑问,我将寻求表达此想法的最佳方法。

3 个答案:

答案 0 :(得分:2)

您可以使用递归函数

const data = [{
    "id": 1,
    "email": "a@b.com", // unique
    "parent": "c@b.com" // is nullable
  },
  {
    "id": 2,
    "email": "b@b.com",
    "parent": null
  },
  {
    "id": 3,
    "email": "c@b.com",
    "parent": "b@b.com"
  },
  {
    "id": 4,
    "email": "d@b.com",
    "parent": "a@b.com"
  },

]

const order = (arr, level) => {
  const children = arr.filter(e => e.parent === level); // get the elements that have the same parent ( level )
  const parent = arr.find(e => e.email === level); // get the parent
  
  return children.length 
    ? [
        ...children.map(e => 
          ({ ...e,
            parent: parent ? parent.id : null // update the parent to the id instead of email
          })),
        ...order(arr, children[0].email) // call the same function with the email of the first child of the current children array, it will become a parent
      ] 
    : children // otherwise return the array
}

const result = order(data, null)

console.log(result)

答案 1 :(得分:2)

我将通过首先生成一个新输入(用parent email替换parent id以及与它们所属的树相关的节点级别的新属性)来解决这个问题。然后,我们可以通过此level属性对节点进行排序,并在相等的level上通过id进行排序。

const input = [
    {"id": 1, "email": "a@b.com", "parent": "c@b.com"},
    {"id": 2, "email": "b@b.com", "parent": null},
    {"id": 3, "email": "c@b.com", "parent": "b@b.com"},
    {"id": 4, "email": "d@b.com", "parent": "a@b.com"},
    {"id": 5, "email": "x@b.com", "parent": "b@b.com"},
    {"id": 6, "email": "z@b.com", "parent": "x@b.com"},
    {"id": 7, "email": "y@b.com", "parent": null},
    {"id": 8, "email": "m@b.com", "parent": "y@b.com"}
];

const findParent = (mail) => input.find(x => x.email === mail);

const getLevel = (mail, lvl) =>
{    
    return mail ? getLevel(findParent(mail).parent, lvl + 1) : lvl;
}

let newInput = input.map(({id, email, parent}) =>
{
    return {
        id: id,
        email: email,
        parent: findParent(parent) ? findParent(parent).id : null,
        lvl: getLevel(parent, 0)
    };
});

let sortedInput = newInput.sort((a, b) =>
{
    return (a.lvl - b.lvl) ? a.lvl - b.lvl : a.id - b.id;
});

console.log(sortedInput);

答案 2 :(得分:1)

下面是一种迭代方法(与提供的递归解决方案相对),您可以用来实现结果。基本上,从查找根元素开始,然后遍历原始数组,查找以当前元素为父元素的元素。

要用ID替换父电子邮件,只需将父名称映射到ID:

var data = [{
  "id": 1,
  "email": "a@b.com", // unique
  "parent": "c@b.com" // is nullable
}, {
  "id": 2,
  "email": "b@b.com",
  "parent": null
}, {
  "id": 3,
  "email": "c@b.com",
  "parent": "b@b.com"
}, {
  "id": 4,
  "email": "d@b.com",
  "parent": "a@b.com"
}]

//Map email addresses to IDs
var map = data.reduce((accum, el) => {
  accum[el.email] = {
    id: el.id
  }
  return accum;
}, {});


var [root] = data.filter(el => !el.parent);
var users = [root];
var cur;
var children;
while (users.length < data.length) {
  cur = users[users.length - 1];
  //Find elments that have cur as parent
  children = data.filter(el => el.parent === cur.email);
  children.forEach(el => {
    users.push({
      id: el.id,
      email: el.email,
      parent: map[el.parent].id
    });
  });
}

console.log(users)