Merge multiple objects?

Hello everyone,

I’m trying to tackle an algorithm problem and I am running into difficulty.

Here is my code so far:

const classNames = [
  'spacing-xs-top-none',
  'carousel-mouse-drag-true',
  'carousel-xs-display-items-2',
  'carousel-md-display-items-1',
  'padding-md-left-extra',
  'spacing-top-none',
];
const breakpoints = {
  xs: 0,
  sm: 300,
  md: 600,
  lg: 900
}
const finalObj = {};

classNames.forEach((className) => {
  let classBreakpoint = 'all';
  let formattedClassName = className;

  Object.keys(breakpoints).forEach((breakpoint) => {
    if(className.includes(breakpoint)){
      classBreakpoint = breakpoint;
      formattedClassName = className.replace(`-${classBreakpoint}`, '');
    } 
  });

  let variantName = formattedClassName
  .split('-')
  .slice(0, 1)
  .join();

  let variantProperty = formattedClassName
  .split('-')
  .slice(1, -1)
  .join('-');

  let variantValue = formattedClassName
  .split('-')
  .slice(-1)
  .join();

  // create the Object
  const obj = {};
  obj[variantName] = {};
  obj[variantName][classBreakpoint] = {};
  obj[variantName][classBreakpoint][variantProperty] = variantValue;

});
console.log(finalObj);

// desired final object
// {
//   spacing: { xs: { top: none } 
//              all: { top: none }
//            }
//   carousel: { all: { mouse-drag: true } 
//               xs: { display-items: 2 }
//               md: { display-items: 1 }
//             }
//   padding: { md: { left: extra } }
// }

The challenge is to loop through the array of classnames and produce an object in the structure of the desired final object provided.

I have gotten to the point where I am creating each classname in the correct format. For example the first loop is returning an object like so:

{ spacing: { xs: { top: 'none' } } }

Which is as desired. My problem is when trying to combine the objects into the final result. I have tried using

Object.assign(finalObj, obj);

at the end of each for loop but it causes key value pairs that already exist in the object to be overwritten.

Output of finalObj

{ spacing: { all: { top: 'none' } },
  carousel: { md: { 'display-items': '1' } },
  padding: { md: { left: 'extra' } } }

Is there a clever way to merge objects without the key value pairs being overwritten? Or is there a better way of tackling this?

Any help appreciated!

Slight update.

I have managed to get the correct output with the following but I don’t feel the solution is that good :frowning:

const classNames = [
  'spacing-xs-top-none',
  'carousel-mouse-drag-true',
  'carousel-xs-display-items-2',
  'carousel-md-display-items-1',
  'padding-md-left-extra',
  'spacing-top-none',
];
const breakpoints = {
  xs: 0,
  sm: 300,
  md: 600,
  lg: 900
}
const finalObj = {};

classNames.forEach((className) => {
  let classBreakpoint = 'all';
  let formattedClassName = className;

  Object.keys(breakpoints).forEach((breakpoint) => {
    if(className.includes(breakpoint)){
      classBreakpoint = breakpoint;
      formattedClassName = className.replace(`-${classBreakpoint}`, '');
    } 
  });

  let variantName = formattedClassName
  .split('-')
  .slice(0, 1)
  .join();

  let variantProperty = formattedClassName
  .split('-')
  .slice(1, -1)
  .join('-');

  let variantValue = formattedClassName
  .split('-')
  .slice(-1)
  .join();

  // create the Object
  const obj = {};

  if(!finalObj.hasOwnProperty(variantName)){
    obj[variantName] = {};
  } else {
    obj[variantName] = finalObj[variantName];
  }

  if(!obj[variantName].hasOwnProperty(classBreakpoint)){
    obj[variantName][classBreakpoint] = {};
  } else {
    obj[variantName][classBreakpoint] = finalObj[variantName][classBreakpoint];
  }

  obj[variantName][classBreakpoint][variantProperty] = variantValue;

  Object.assign(finalObj, obj);

});
console.log(finalObj);

// desired final object
// {
//   spacing: { xs: { top: none } 
//              all: { top: none }
//            }
//   carousel: { all: { mouse-drag: true } 
//               xs: { display-items: 2 }
//               md: { display-items: 1 }
//             }
//   padding: { md: { left: extra } }
// }

Hey @heenslice, before we get to the object, check this:

let variantProperty = formattedClassName
  .split('-')
  .slice(1, -1)
  .join('-');
  1. What if your class name will include -? Like carousel-intro
  2. What if your property name will include -? Like border-box

Hi,

Thanks for responding.

And good catch! I suppose in those cases the object would not be formatted correctly. I was mostly focusing on getting the test cases provided to work but ideally I would be able to handle cases like border-box

Do you have any suggestions on how I can split the string in a more robust way to cover these cases?

Maybe you can separate them by a character that can’t be used in normal css variables, like ;.

Now, to the object :slight_smile:
Normally if you want to combine objects, you do this:

nest = (o, to) => Object.assign(to, o);

Fairly easy, but as you mentioned it will overwrite all nested objects. So you probably need to ‘deepify’ it:

nestDeep = (o, to) => {
    // exit condition for recursion
    if (typeof to !== 'object') return o;
    // let's check every key in object we want to put in
    for (let k in o) {
    // if we already have this key and what we want to put in is object - recurse
		if (to.hasOwnProperty(k) && typeof o[k] === 'object') {
			nestDeep(o[k], to[k])
        } else {
    // else, just put whatever in
			to[k] = o[k];
        }
    }
    // return result
	return to;
}

const a = { user: { name: 'iiiked', mood: ':(' } };
const b = { user: { age: 35, mood: ':)' , mode: 'dev' } };

console.log(nestDeep(b, a));

Good luck with parsing dashes :slight_smile:

Awesome,

Not sure if I really understand what is going on here yet but thanks for the response!

In your code you can do like this:

// Create object
const obj = {};
obj[variantName] = {};
obj[variantName][classBreakpoint] = {};
obj[variantName][classBreakpoint][variantProperty] = variantValue;

nestDeep(obj, finalObj);