-7

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", ...])

Here's the desired outcome...

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"}
        }
    }
}

...based on this data.

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"
  }
];

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"])

Ivan
  • 1,967
  • 4
  • 34
  • 60
  • 1
    Where is your `deepNest` ? – mplungjan Dec 01 '20 at 06:41
  • What do you mean? That is my question, the actual `deepNest` function. I don't know how do to it. – Ivan Dec 01 '20 at 06:43
  • 2
    [Stack Overflow is not a code writing service](https://meta.stackexchange.com/questions/69798/is-stack-overflow-a-code-writing-service). https://idownvotedbecau.se/noattempt/ https://idownvotedbecau.se/nomcve/ – Samathingamajig Dec 01 '20 at 06:50
  • 2
    Please visit the [help], take the [tour] to see what and [ask]. Do some research, search for related topics on SO; if you get stuck, post a [mcve] of your attempt, noting input and expected output using the `[<>]` snippet editor. – mplungjan Dec 01 '20 at 06:51
  • 3
    It is a rule of Stack Overflow that you need to show an attempt and not just say "here, do this for me". What I'll do for you is give you tips: look into using `Array.prototype.sort()` and `Array.prototype.filter()`, also indexing objects with strings: `window.console.log("oh wow!")` is the same as `window["console"]["log"]("oh wow!")`, things like that. – Samathingamajig Dec 01 '20 at 06:55
  • Yeah, [LOVE THIS minimum example](https://stackoverflow.com/questions/18017869/build-tree-array-from-flat-array-in-javascript)... – Ivan Dec 01 '20 at 06:58
  • By the way, that post was made 7 years, 4 months ago. Stack Overflow has changed a lot since then. – Samathingamajig Dec 01 '20 at 07:00
  • We need to see: 1) Your attempt at a `deepNest()` function, based on your specification, 2) What the given output is, and 3) What's wrong with the given output/what it should be. – Samathingamajig Dec 01 '20 at 07:03
  • So do you want the last section to be an object or an array? your example output has it as an object, but it is indexed like an array. I'm considering making this... – Samathingamajig Dec 01 '20 at 07:06
  • It's a legit question, that there's no answer to from what I could find. Rather than me asking the same sorting/grouping question a thousand times, I am asking something really difficult for me to even attempt, that's super useful. As for the object vs array it doesn't really matter, as long as it's nested. From what I've seen, the object route is better since the values can be referenced easier, but not that important since I already have the nesting beforehand. – Ivan Dec 01 '20 at 07:08
  • This was the [inspiration for the question](https://learnwithparam.com/blog/how-to-group-by-array-of-objects-using-a-key/), I didn't want to post that code as my attempt because it's dishonest. – Ivan Dec 01 '20 at 07:12
  • 1
    By the way, what is the use case of this? I can't think of one, but you probably do have a use case since you say it's "super useful". ¯\\_(ツ)\_/¯ – Samathingamajig Dec 01 '20 at 08:13
  • The use case is generating a nested table. Ex. the widget fetches the raw data, and then the user applies a `groupBy` setting, or more. I want the UI to reflect the data, rather than build the logic in the UI, which I don't even know how to begin thinking about. I believe this is a better approach as manipulating an array is much faster than shuffling UI elements. Correct me if I'm wrong. – Ivan Dec 01 '20 at 08:28
  • 1
    Late to the party buddy. – Ivan Dec 01 '20 at 13:14

1 Answers1

3

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;
}
Samathingamajig
  • 11,839
  • 3
  • 12
  • 34
  • 1
    @Ivan I have updated how the data is displayed so hopefully it looks better. (Adding stuff to the DOM instead of console.log'ging looks better imo for things like this) – Samathingamajig Dec 01 '20 at 07:50
  • 1
    "I will try to understand how it works" ask some questions if you want to know more. I don't know what your skill level is so I don't know what to explain more. – Samathingamajig Dec 01 '20 at 07:51
  • 1
    Stop, I can only get so erect!!! My level is good enough to understand your code, not good enough to come up with it. This code is a work of art, **seriously**. This line `...arr.reduce((set, val) => set.add(val[first]), new Set())` daaaaamn... I get everything, I just can't create it, thus my question. Incredible work, I am hyped. I aspire to have the wit to even come close to this elegance. – Ivan Dec 01 '20 at 08:03
  • 1
    I have made this slightly better by sorting the array of options alphabetically so that every child has the keys in the same order as each other. It doesn't do anything data-wise, but 1) it looks better/cleaner for the presentation, 2) reason #1 was the only reason. – Samathingamajig Dec 01 '20 at 08:11
  • 2
    @mplungjan By "presentation" do you mean the simple css and [displaying to the DOM]? Sure, why not? I didn't invent css or anything, but the `whitesmoke` main background, the white background `
    ` tag with 1rem padding, a 2px solid black border, and a radius of 1rem is the style of a webapp I'm working on, so I brought it over to make this look decent. You can definitely use that stuff. (`JSON.stringify(aJSONObject, null, spacesToIndent)` is a good function to stringify JSON with proper indentation, and I definitely didn't make that)
    – Samathingamajig Dec 01 '20 at 08:37
  • I know the stringify and keep forgetting it has parameters :) Thanks – mplungjan Dec 01 '20 at 08:38