0

I am trying to implement in Tabulator JS an opportunity for user to reconstruct from a table a new one with tree structure. So tableData is an assumed table and columnTree is user's choice which column has to be the tree root.

My idea is firstly to store unique key values in an array, then loop through the tableData filtering it by each key, add to the first element [_children] key with values of the rest of the filtered objects.

var columnTree = "continent";

var tableData = [
                {continent:"Asia", country:"China"},
                {continent:"Asia", country:"Vietnam"},
                {continent:"Asia", country:"Thai"},
                {continent:"America", country:"USA"},
                {continent:"America", country:"Canada"},
                {continent:"Africa", country:"Egypt"},
                {continent:"Africa", country:"Somalia"},
            ];

Get unique keys

var unique = tableDataNested.map(item => item[columnTree]).filter((value, index, self) => self.indexOf(value) === index);

// ["Asia", "America", "Afrika"]

Filter function

function filterByValue(array, string) {
                return array.filter(o =>
                    Object.keys(o).some(k => o[k].toLowerCase().includes(string.toLowerCase())));
            };

The last step

var finalArray = [];
unique.forEach(function(continent) {
                //temporary array with values of each continent 
                var tempArray = filterByValue(tableDataNested, continent);
                console.log(tempArray);
                //get the first object
                console.log(tempArray[0]);
                //add to the first object '_children' key with values of the rest of objects except the 
                //first.
                var finalResult = tempArray[0]['_children'] = [tempArray.slice(1)];
                console.log(finalResult);
                finalArray.push(finalResult);
            });

The thing is, it crashes on the second continent and I can't get why

VM480:3 Uncaught TypeError: o[k].toLowerCase is not a function
    at <anonymous>:3:51
    at Array.some (<anonymous>)
    at <anonymous>:3:36
    at Array.filter (<anonymous>)
    at filterByValue (<anonymous>:2:30)
    at <anonymous>:2:33
    at Array.forEach (<anonymous>)
    at <anonymous>:1:8

My ideal result is

var finalArray = [
                {continent:"Asia", country:"China", "_children": [
                    {continent:"Asia", country:"Vietnam"},
                    {continent:"Asia", country:"Thai"}
                ]},
                {continent:"America", country:"USA", "_children": [
                    {continent:"America", country:"Canada"},
                ]},
                {continent:"Africa", country:"Egypt",  "_children": [
                    {continent:"Africa", country:"Somalia"}
                ]}
            ];

2 Answers 2

2

You might be better off using a generic groupBy function, for example

function groupBy(ary, fn) {
    let m = new Map();
    for (let x of ary) {
        let key = fn(x);
        if (!m.has(key))
            m.set(key, []);
        m.get(key).push(x);
    }
    return m;
}

and then

let groupMap = groupBy(tableData, x => x[columnTree]);
let result = [];

for (let [first, ...rest] of groupMap.values()) {
    first._children = rest;
    result.push(first);
}

Note that this also mutates the original tableData array, if this is not desirable, change the loop body to:

result.push({...first, _children: rest});
Sign up to request clarification or add additional context in comments.

Comments

1

The simpler way is using .reduce to aggregate your data.

The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.

var tableData = 
[
  {continent:"Asia", country:"China"},
  {continent:"Asia", country:"Vietnam"},
  {continent:"Asia", country:"Thai"},
  {continent:"America", country:"USA"},
  {continent:"America", country:"Canada"},
  {continent:"Africa", country:"Egypt"},
  {continent:"Africa", country:"Somalia"},
];

var result = tableData.reduce((acc, item) => {
  if(acc[item.continent] === undefined)
     acc[item.continent] = { continent: item.continent, country: item.country, _children: []}
  else
    acc[item.continent]._children.push(item);
  return acc;
}, {});

console.log(Object.values(result));


  

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.