eymas
eymas

Reputation: 105

Angular 8 - Sorting a recursive tree component by type of value

Given the following object:

const data = {
    "root": {
         "title": "Shapes",
         "shapes": { ... },
         "description": "All sorts of geometrical whizz"
    }
}

I'd need to find a way to sort this object to have the keys with objects appear above the keys with strings, such as below:

const data = {
    "root": {
         "shapes": { ... },
         "title": "Shapes",
         "description": "All sorts of geometrical whizz"
    }
}

What I have tried was the sort() function:

const sorted = Object.values(root).sort((v) => typeof v === 'object' ? -1 : 1)

While this works to sort the values while keeping the root object pristine, the keys are lost in this new sorted array...

[
    {...}, 
    "Shapes", 
    "All sorts of geometrical whizz"
]

...while it must be kept as an object as the keys are used by a recursive Tree Component, where sorting like this is moot:

<ng-template *ngFor="let key of Object.keys(data)">
    <div *ngIf="typeof key === 'object'>
        <button (click)="show(value)">
            {{key}} 
        </button>
        <this-template [data]="data[key]">
    </div>

    <div *ngIf="typeof key === 'string'>
        <button (click)="show(value)">
            {{key}} 
        </button>
    </div>
</ng-template>

as the component will still render based on the structure of root:

↓ root
    |- title
    |- > shapes
    |- description

while the desired structure is this:

↓ root
    |- > shapes
    |- title
    |- description

Thus, to define the problem in a single question;

How would I be able to sort an object by the type of the key's value, in order to render a recursive component to display items with objects before items with strings?

Upvotes: 2

Views: 1351

Answers (3)

Girgis A.Jacoub
Girgis A.Jacoub

Reputation: 96

A bit shorter answer

let data = {
  "root": {
    "title": "Shapes",
    "shapes": { key: 'value' },
    "description": "All sorts of geometrical whizz"
  }
};

/*
 * looping through your `root` object's keys
 * if the value of the key is object then assign the whole `data.root` object to a new object which starts with
 * that key-value pair
 */
Object.keys(data.root).map(key => {
  if ((typeof data.root[key]) === "object")
    data.root = Object.assign({ [key]: data.root[key] }, data.root);
});

Upvotes: 1

Teddy Sterne
Teddy Sterne

Reputation: 14239

By using Object.entries and a reduce you should be able to achieve the desired result:

const data = {
  "root": {
    "shapes": { /* ... */ },
    "title": "Shapes",
    "description": "All sorts of geometrical whizz"
  }
}

const sorted = Object.entries(data.root)
  .sort(([key, v]) => typeof v === 'object' ? -1 : 1)
  .reduce((newObj, [key, v]) => ({ ...newObj, [key]: v }), {})
  
console.log(sorted)

Upvotes: 1

Ivan Mihaylov
Ivan Mihaylov

Reputation: 433

A not so elegant but fast solution would be

    const data = {
      "root": {
           "title": "Shapes",
           "shapes": { a: 'name' },
           "description": "All sorts of geometrical whizz"
      }
  };

  const object = {};

  Object.values(data.root).forEach((a, i) => {
    // console.log(a);
    if (typeof(a) === 'object') {
      object[Object.keys(data.root)[i]] = a;
    }
  });

  Object.values(data.root).forEach((a, i) => {
    // console.log(a);
    if (!(typeof(a) === 'object')) {
      object[Object.keys(data.root)[i]] = a;
    }
  });
  console.log(object);

Upvotes: 1

Related Questions