1

I've got two javascript objects, i want to change the structure of one of them and merge the other one into the first one

I've tied using loops but it doesn't seem to be the best way to do it

My first object is called 'navbar'

navbar:
   [ { Id: 7,
       ParentId: null,
       Name: 'Home',
       Slug: '/',
       Priority: 1,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Custom Plus' },
     { Id: 15,
       ParentId: 14,
       Name: 'About Us',
       Slug: 'about-us',
       Priority: 1,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Custom Plus' },
     { Id: 8,
       ParentId: null,
       Name: 'New Cars',
       Slug: 'new-cars',
       Priority: 2,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Custom' },
     { Id: 14,
       ParentId: null,
       Name: 'More',
       Slug: 'more',
       Priority: 8,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Placeholder' } ]

And my second one is called newvehicles:

newvehicles:
   [ { Id: 5,
       VehicleType: 'Car',
       MakeId: 5,
       MakeName: 'Make',
       MakeSlug: 'make',
       ModelId: 13,
       ModelName: 'All-New Car',
       ModelSlug: 'all-new-car',
       Prefix: null,
       Suffix: null,
       Priority: 1,
       Redirect: null,
       Image:
        'http://xxxx.jpg',
       Assets: null,
       Markup: null },
     { Id: 6,
       VehicleType: 'Car',
       MakeId: 5,
       MakeName: 'Car',
       MakeSlug: 'Car',
       ModelId: 8,
       ModelName: 'Car',
       ModelSlug: 'Car',
       Prefix: null,
       Suffix: null,
       Priority: 2,
       Redirect: null,
       Image:
        'http://xxxx.jpg',
       Assets: null,
       Markup: null } ] 

What i want to achieve is for the object of newvehicles to be pushed into the navbar as a child of 'new-cars' and also to go through the navbar and move anything that has a ParentId of not null to be moved into that parent object as a child

I'm currently using a for loop to do some of the changes:

    for (var i = 0; i < navigation.navbar.length; i++) {
      var obj = navigation.navbar[i];
      if (obj.Slug == 'new-cars') {
        obj.child = newvehicles;
      }
    }

But i'm looking for the output to be something like this:

navbar:
   [ { Id: 7,
       ParentId: null,
       Name: 'Home',
       Slug: '/',
       Priority: 1,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Custom Plus' },
     { Id: 8,
       ParentId: null,
       Name: 'New Cars',
       Slug: 'new-cars',
       Priority: 2,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Custom'.
child :  [ { Id: 5,
       VehicleType: 'Car',
       MakeId: 5,
       MakeName: 'Make',
       MakeSlug: 'make',
       ModelId: 13,
       ModelName: 'All-New Car',
       ModelSlug: 'all-new-car',
       Prefix: null,
       Suffix: null,
       Priority: 1,
       Redirect: null,
       Image:
        'http://xxxx.jpg',
       Assets: null,
       Markup: null },
     { Id: 6,
       VehicleType: 'Car',
       MakeId: 5,
       MakeName: 'Car',
       MakeSlug: 'Car',
       ModelId: 8,
       ModelName: 'Car',
       ModelSlug: 'Car',
       Prefix: null,
       Suffix: null,
       Priority: 2,
       Redirect: null,
       Image:
        'http://xxxx.jpg',
       Assets: null,
       Markup: null } ]        },
     { Id: 14,
       ParentId: null,
       Name: 'More',
       Slug: 'more',
       Priority: 8,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Placeholder',
child:     { Id: 15,
       ParentId: 14,
       Name: 'About Us',
       Slug: 'about-us',
       Priority: 1,
       MenuDisplay: true,
       MenuSubDisplay: false,
       MobileDisplay: false,
       PageType: 'Custom Plus' }       } ]
random_user_name
  • 25,694
  • 7
  • 76
  • 115
Tam2
  • 323
  • 1
  • 5
  • 16
  • Is the order of nav elements consistent / guaranteed in your code? If so, it's as simple as `navbar[2].child = newvehicles;` - if not, then you'll have to find the index first, then assign it... – random_user_name Jul 11 '19 at 20:00
  • Nope order isn't guranteed which is why I was using the for loop to find the index and then set the child. Would I also need to do a for loop for the second part? Finding all items with a parentID then finding the element with that ID and inserting it as a child and removing it from the main array? – Tam2 Jul 11 '19 at 20:24
  • Then this solution (using `findIndex`) combined with my code above, would do it: https://stackoverflow.com/q/15997879/870729 – random_user_name Jul 11 '19 at 20:29
  • 1
    Those are JS objects. JSON is always a string. –  Jul 11 '19 at 20:39

2 Answers2

2

The looping logic you presented already seems to be meeting your goal regarding appending the cars logic. However, implicit in your request is that there will only be one entry with a slug of 'new-cars'. If that is true, then consider:

navbar.find(item => item.Slug == 'new-cars').child = newvehicles;

Regarding the non-null parentId, you can:

  • loop backwards through the outer array,
  • within the loop, find any array items that have an id matching the current item's parentId,
  • splice out the current item (the child)
  • load it into the to the retrieved parent's 'child' property,

Here is the code:

for (var i = navbar.length - 1; i >= 0; i--) {
  var obj = navbar[i];
  let parent = navbar.find(item => item.Id == obj.ParentId);
  if (parent) {
    parent.child = navbar.splice(i,1);    
  }
}

In both cases, prefix 'navigation.' to 'navbar' and/or 'newvehicles' as necessary.

Edit: To deal with the OP's comment below requesting the ability to associate multiple children with a parent.

In order for the 'child' property to allow multiple children, we would turn it into an array. So we would first check to see if the 'child' property exists in the parent. If not, we create it and set it equal to an empty array. Then, we push the spliced child object inside the loop:

for (var i = navbar.length - 1; i >= 0; i--) {
  var obj = navbar[i];
  let parent = navbar.find(item => item.Id == obj.ParentId);
  if (parent) {
    if (!parent.child)
        parent.child = [];
    parent.child.push(navbar.splice(i,1)[0]);    
  }
}

Consider changing the name of 'child' to 'children' for your needs.

Also, be aware that all of this will only work for one level. If you need multi-level nesting of parents to children, then a simple loop will not suffice. You may want to create a different question in that case.

pwilcox
  • 5,542
  • 1
  • 19
  • 31
  • Is there a way to push the items into Child, at the moment if there are two items with the same parent ID then only one of them get's added as the child rather than both of them as an array – Tam2 Jul 16 '19 at 19:41
  • Thanks for the updated code, with that method i end up with a double array as the child, put up some sample data here - https://jsfiddle.net/0mrtvjs9/2/ – Tam2 Jul 19 '19 at 07:39
  • @TamoorMalik, I added '[0]' after the splice, which should do the trick. – pwilcox Jul 19 '19 at 15:32
0

Here is a solution based on Lodash-fp

This a variant of Lodash library which allows a full functional programming style and makes this kind of tasks so much easier (don't hesitate if you wish some explanations)

const parentsPartition = _.partition(['ParentId', null], navbar);
const vehicleHierarchy = _.map(
    vehicle => {
        const children = _.filter(['ParentId', vehicle.Id], parentsPartition[1]);
        return _.cond([
            [_.isEmpty, _.constant(vehicle)],
            [_.stubTrue, _.set('children', _, vehicle)]
        ])(children);
    },
    _.concat(parentsPartition[0], newvehicles)
);

And here the result (sorry, my screen isn't high enough to full print)

enter image description here

Shizzen83
  • 3,325
  • 3
  • 12
  • 32