将对象的平面数组转换为具有动态结构的深层嵌套对象(树)

问题描述:

我为静态结构找到了很多解决方案,但对于动态结构却一无所获.这是一个简单的用例...

I've found a lot of solutions for a static structure, but nothing for a dynamic one. Here's the simple use case...

let nested = deepNest(array, ["criteria1", "criteria2", ...])

这是想要的结果...

deepNest(people, ["gender", "color"]);

// result
{
    male: {
        blue: {
            0: {name: "Jim", color: "blue", gender: "male"},
            1: {name: "Sam", color: "blue", gender: "male"}
        },
        green: {
            0: {name: "Eddie", color: "green", gender: "male"}
        }
    },
    female: {
        blue: {
            0: {name: "Eva", color: "blue", gender: "female"},
        },
        green: {
            0: {name: "Susan", color: "green", gender: "female"}
        }
    }
}

...基于此数据.

const people = [
  {
    name: "Jim",
    color: "blue",
    gender: "male"
  },
  {
    name: "Susan",
    color: "green",
    gender: "female"
  },
  {
    name: "Sam",
    color: "blue",
    gender: "male"
  },
  {
    name: "Eddie",
    color: "green",
    gender: "male"
  },
  {
    name: "Eva",
    color: "blue",
    gender: "female"
  }
];

请记住,分组必须是动态的,即数组可以嵌套,如 deepNest(people, ["gender"])deepNest(人物,[颜色",性别",姓名"])

Keep in mind that the grouping has to be dynamic i.e. the array can be nested like deepNest(people, ["gender"]) or deepNest(people, ["color", "gender", "name"])

下面是一些使用递归的代码.它首先找到第一个查询的所有选项,然后使用给定查询具有相同值的对象数组递归调用自身,并且在最深点,如果没有更多查询,则返回对象数组满足所有先前的查询.

Here's some code that works with recursion. It first finds all the options for the first query, and then recursively calls itself with the array of the objects with the same value for the given query, and at the deepest point, if there is no more queries, it returns the array of objects that meet all the previous queries.

我可以回答有关此的任何问题,但我觉得在评论和此描述中对此进行了足够的解释.

I can answer any questions about this, but I feel like this is explained enough in the comments and this description.

const people = [
  {
    name: "Jim",
    color: "blue",
    gender: "male"
  },
  {
    name: "Susan",
    color: "green",
    gender: "female"
  },
  {
    name: "Sam",
    color: "blue",
    gender: "male"
  },
  {
    name: "Eddie",
    color: "green",
    gender: "male"
  },
  {
    name: "Eva",
    color: "blue",
    gender: "female"
  }
];



const deepNest = (arr, [first, ...rest]) => {
  let output = {};
  if (first) {
    let options = [...arr.reduce((set, val) => set.add(val[first]), new Set())].sort(); // Get the options (Set() is a built in thing to remove duplicates), and the sort them alphabetically
    for (let option of options) {
      let val = arr.filter((val) => val[first] === option); // Get the values that have the same value for the option for the query
      output[option] = deepNest(val, rest); // Recursion
    }
  } else {
    output = arr;
  }
  return output;
}

display(deepNest(people, ["gender", "color"]), ["gender", "color"]);
display(deepNest(people, ["color", "gender"]), ["color", "gender"]);
display(deepNest(people, ["color", "name", "gender"]), ["color", "name", "gender"]);

// My own custom display function, since console.log() makes it look wack
function display(json, query) {
  const div = document.createElement("div");
  const h2 = document.createElement("h2");
  h2.innerText = `["${query.join("\", \"")}"]`;
  const pre = document.createElement("pre");
  pre.innerText = JSON.stringify(json, null, 2);
  
  div.appendChild(h2);
  div.appendChild(pre);
  
  document.querySelector("body").appendChild(div);
}

html,
body {
  background: whitesmoke;
}

pre {
  color: black;
  background: white;
  border: 2px solid black;
  border-radius: 1rem;
  padding: 1rem;
}