2

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?

0

3 Answers 3

1

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)

Sign up to request clarification or add additional context in comments.

3 Comments

This will also change the string properties order. I think you should return '0' instead '1' in your sort
It does change the order of strings to descending order when using 1, but changing it to 0 somehow makes the sorting ineffective. The same effect occurs when replacing -1 with 0 and shuffling about. That being said, I did try to chain the sort with a reduce but have struggled with creating the keys in the resulting newObj object, Teddy Sterne 's example fits as the final piece to the puzzle. The .reduce() function is very powerful, but does need some training to use!
To further elaborate on the string order, it appears that running the semi-oneliner in the application (typescript) with ? -1 : 1 does sort the strings in descending order (last to first - top to bottom) but in a node REPL the order is unchanged. interesting!
1

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);

Comments

1

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);
});

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.