6

I want to merge two objects, override properties but keep properties that are not been overridden.

Example: I have the following Objects

const theme = {
 colors: {
  base: '#fff',
  accent: '#ff0000'
 }
}

and

const themeOverride = {
 colors: {
  accent: '#ff8900'
 }
}

and would like to merge these together to get

const newTheme = {
  colors: {
   base: '#fff',
   accent: '#ff8900'
  }
}
furtner
  • 115
  • 2
  • 7

7 Answers7

5

If you just want to merge the property color of theme and themeOverride, you can do it by the code below:

var theme = {
 colors: {
  base: '#fff',
  accent: '#ff0000'
 }
};
var themeOverride = {
 colors: {
  accent: '#ff8900'
 }
};
Object.assign(theme.colors, themeOverride.colors);
console.log(theme);
Faly
  • 13,291
  • 2
  • 19
  • 37
  • 2
    While this answers the specific example in the question, it doesn't really help in the broader sense, given the example is called `theme` and `colors`, it's probably safe to assume there will be further sub-properties (eg `fonts`, `borders` and the like) that would also need to be merged. – James Thorpe Nov 21 '17 at 10:12
5

You can use Object.assign to merge these objects

Update existing object

const theme = {
  colors: {
    base: '#fff',
    accent: '#ff0000'
  }
}

const themeOverride = {
  colors: {
    accent: '#ff8900'
  }
}

Object.assign(theme.colors, themeOverride.colors)

console.log(theme)

Or create new object

const theme = {
  colors: {
    base: '#fff',
    accent: '#ff0000'
  }
}

const themeOverride = {
  colors: {
    accent: '#ff8900'
  }
}

newTheme = { colors: Object.assign({}, theme.colors, themeOverride.colors) }

console.log(newTheme)
Mikhail Katrin
  • 2,304
  • 1
  • 9
  • 17
2

You could merge by iterateing all properties for update with a recursive approach for objects.

function merge(target, source) {
    Object.keys(source).forEach(function (key) {
        if (source[key] && typeof source[key] === 'object') {
            merge(target[key] = target[key] || {}, source[key]);
            return;
        }
        target[key] = source[key];
    });
}

var theme = { colors: { base: '#fff', accent: '#ff0000' } }, 
    themeOverride = { colors: { accent: '#ff8900' } };
    
merge(theme, themeOverride);

console.log(theme);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

JS doesn't have a built-in way to do this, but it's very simple to do with Lodash, or Underscore's _.merge() or Ramda's _.mergeDeepLeft(), all of which recursively merge objects.

const theme = {
 colors: {
  base: '#fff',
  accent: '#ff0000'
 }
}

const themeOverride = {
 colors: {
  accent: '#ff8900'
 }
}

const newTheme = _.merge(theme, themeOverride);

console.log(newTheme);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
JLRishe
  • 99,490
  • 19
  • 131
  • 169
0

You can use reduce with old theme as initial value. Try something like this:

const theme = {
  colors: {
    base: '#fff',
    accent: '#ff0000'
  },
}

const themeOverride = {
  colors: {
    accent: '#ff8900'
  },
  border: {
    borderWidth: '2px'
  }
}

const newTheme = Object.keys(themeOverride).reduce((prev, key) => {
  prev[key] = Object.assign({}, theme[key] || {}, themeOverride[key])
  return prev
}, Object.assign({}, theme))

console.log(newTheme)

Note, this solution expects maximum 2 level nesting.

dfsq
  • 191,768
  • 25
  • 236
  • 258
0

Iterate both objects, look for intersection and override in that instance; else, just copy as such;

const theme = {
 colors: {
  base: '#fff',
  accent: '#ff0000'
 }
}

const themeOverride = {
 colors: {
  accent: '#ff8900'
 }
}
window.onload = mergeObjects(theme,themeOverride)

function mergeObjects(base,override) {
   var mergedObj = {'colors' : {}};
   for(key in base["colors"]) {
      if(override['colors'][key] == undefined) {
        mergedObj['colors'][key] = base['colors'][key]
      }
      else {
        mergedObj['colors'][key] = override['colors'][key]
      }
   }
   console.log('mergedObject is',mergedObj)
}
Rajkumar Somasundaram
  • 1,225
  • 2
  • 10
  • 20
0

You could recursively look through your object and assign your new updated values this way.

Here, I made a function for it :

const theme = {
 colors: {
  base: '#fff',
  accent: '#ff0000'
 }
}

const themeOverride = {
 colors: {
  accent: '#ff8900'
 }
}

function overrideObject(o1,o2){
  var res = {};
  //Go through all your attributes
  for (var a in o1){
    //Begin recursive method if another object is detected
    if(typeof o1[a] == 'object'){
      res[a] = overrideObject(o1[a],o2[a])
    }
    //Clone old data & update it if necessary
    else{
      res[a] = o1[a]; 
      if(typeof o2[a] != 'undefined') res[a] = o2[a]; 
    }
  }
  
  return res;
}

console.log(overrideObject(theme,themeOverride));
Zenoo
  • 12,670
  • 4
  • 45
  • 69