Merge Objects Maciej Cieslar

Benchmark created by maciejcieslar on


Setup

const obj1 = {
    _id: '5bede99c0e1d33c83e6268c8',
    index: 0,
    guid: '0fb8cedb-2d45-4d6f-8ddb-6c8680588235',
    isActive: true,
    balance: '$3,822.80',
    picture: 'http://placehold.it/32x32',
    age: 26,
    eyeColor: 'blue',
    name: {
      first: 'Sally',
      last: 'Drake',
    },
    company: 'SECURIA',
    email: 'sally.drake@securia.net',
    phone: '+1 (870) 428-3009',
    address: '219 Rutledge Street, Barronett, Utah, 2541',
    about:
      'Nisi labore nostrud esse aliqua voluptate ipsum magna quis eu incididunt nulla cupidatat. Magna nisi et sint excepteur magna ea velit adipisicing culpa. Exercitation cupidatat do sunt sit eiusmod veniam exercitation. Ea sit voluptate adipisicing excepteur amet cillum aliquip amet. Et esse voluptate labore laborum ex velit eiusmod culpa enim amet ut eu excepteur commodo. Aliquip enim deserunt labore ad non mollit ullamco nulla id laboris qui est.',
    registered: 'Monday, August 10, 2015 12:25 PM',
    latitude: '-81.164075',
    longitude: '79.493894',
    tags: ['aliqua', 'ad', 'enim', 'excepteur', 'ex'],
    range: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    friends: [
      {
        id: 0,
        name: 'Levy Brooks',
      },
      {
        id: 1,
        name: 'Joann Snider',
      },
      {
        id: 2,
        name: 'Peters Miles',
      },
    ],
    greeting: 'Hello, Sally! You have 6 unread messages.',
    favoriteFruit: 'apple',
  }
  
  const obj2 = {
    _id: '5bede99c0e1d33c83e6268c8',
    index: 0,
    guid: '0fb8cedb-2d45-4d6f-8ddb-6c8680588235',
    isActive: true,
    balance: '$3,822.80',
    picture: 'http://placehold.it/32x32',
    age: 26,
    eyeColor: 'blue',
    name: {
      first: 'Molly',
      last: 'Drake',
    },
    company: 'SECURIA',
    email: 'sally.drake@securia.net',
    phone: '+1 (870) 428-3009',
    address: '219 Rutledge Streasdet, Barronett, Utah, 2541',
    about:
      'Nisi labore nostrud esse aliqua voluptate ipsum magna quis eu incididunt nulla cupidatat. Magna nisi et sint excepteur magna ea velit adipisicing culpa. Exercitation cupidatat do sunt sit eiusmod veniam exercitation. Ea sit voluptate adipisicing excepteur amet cillum aliquip amet. Et esse voluptate labore laborum ex velit eiusmod culpa enim amet ut eu excepteur commodo. Aliquip enim deserunt labore ad non mollit ullamco nulla id laboris qui est.',
    registered: 'Monday, August 10, 2015 12:25 PM',
    latitude: '-81.1640s75',
    longitude: '79.4938s94',
    tags: ['aliqua', 'ad', 'enim', 'excepteur', 'ex'],
    range: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    friends: [
      {
        id: 0,
        name: 'Levy Brooks',
      },
      {
        id: 1,
        name: 'Joann Snider',
      },
      {
        id: 3,
        name: 'Peters Miles',
      },
    ],
    greeting: 'Hello, Sally! You have 6 unread messages.',
    favoriteFruit: 'apple',
  }
  
  const isObject = (val) =>
    val !== null && typeof val === 'object' && !Array.isArray(val)
  
  function mergeObject(target, source) {
    Object.keys(source).forEach((key) => {
      const sourceValue = source[key]
      const targetValue = target[key]
  
      target[key] = mergeValues(targetValue, sourceValue)
    })
  
    return target
  }
  
  function mergeArray(target, source) {
    source.forEach((value, index) => {
      target[index] = mergeValues(target[index], value)
    })
  
    return target
  }
  
  function mergeValues(target, source) {
    if (isObject(target) && isObject(source)) {
      return mergeObject(target, source)
    }
  
    if (Array.isArray(target) && Array.isArray(source)) {
      return mergeArray(target, source)
    }
  
    if (source === undefined) {
      return target
    }
  
    return source
  }
  
  const merge1 = (target, ...sources) => {
    sources.forEach((source) => {
      return mergeValues(target, source)
    })
  
    return target
  }
  
  function setAtPath(target, path, value) {
    return path.reduce((result, key, index) => {
      if (index === path.length - 1) {
        result[key] = value
        return target
      }
  
      if (!result[key]) {
        const nextKey = path[index + 1]
  
        result[key] = typeof nextKey === 'number' ? [] : {}
      }
  
      return result[key]
    }, target)
  }
  
  function flatten(collection) {
    return collection.reduce((result, current) => {
      let value = current
  
      if (Array.isArray(current)) {
        value = flatten(current)
      }
  
      return result.concat(value)
    }, [])
  }
  
  const getFlattenedValue = (path = [], value) => {
    if (value === null || typeof value !== 'object') {
      return {
        value,
        path: [...path],
      }
    }
  
    if (Array.isArray(value)) {
      return flattenArrayKeys(path, value)
    }
  
    return flattenObjectKeys(path, value)
  }
  
  function flattenArrayKeys(path, collection) {
    return collection.map((value, index) => {
      return getFlattenedValue([...path, index], value)
    })
  }
  
  function flattenObjectKeys(path, source) {
    return Object.keys(source).map((key) => {
      const value = source[key]
  
      return getFlattenedValue([...path, key], value)
    })
  }
  
  function merge2(target, ...sources) {
    return flatten(
      sources.map((source) => {
        return flattenObjectKeys([], source)
      }),
    ).reduce((result, { path, value }) => {
      if (value === undefined) {
        return result
      }
  
      return setAtPath(result, path, value)
    }, target)
  }

Test runner

Ready to run.

Testing in
TestOps/sec
v1
merge1(obj1, obj2)
ready
v2
merge2(obj1, obj2)
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by maciejcieslar on