递归比较对象并将重复的键值放入数组

时间:2018-12-10 17:08:33

标签: javascript ecmascript-6 lodash

我需要迭代三个对象,以检查重复项是否以单个对象结束。如果有重复项,我想将它们放入数组中,以便用户可以选择最佳值,这样它们最终最终都将成为字符串或整数(或带有字符串/整数的对象)。他们可能看起来像这样:

const a = {
  first_name: 'Tom',
  height: {
    feet: 5,
    inches: 0
  }
}
const b = {
  first_name: 'Thomas',
  last_name: 'Walsh',
  email: 'tomwalsh@domain.com',
  height: {
    feet: 6,
    inches: 0
  }
}
const c = {
  email: 'tomwalsh@sample.edu'
}

结果如下:

const result = {
  first_name: ['Tom', 'Thomas'],
  last_name: 'Walsh',
  email: ['tomwalsh@domain.com', 'tomwalsh@sample.edu'],
  height: {
    feet: [5, 6],
    inches: 0
  }
}

我不知道abc是否是键的权威,所以我不得不假设一组未知的键/值对,但是绝对很小(不超过20对,只有2-3个深度超过3个级别,每个级别最多可以有4-5个键/值对。

我可以创建一个新对象,递归地迭代三个对象,然后转储值或创建值数组,但这似乎效率很低。有什么建议吗?

如果需要,我在项目中有lodash。谢谢!

6 个答案:

答案 0 :(得分:1)

由于某些对象中可能缺少嵌套键,因此您可以通过lodash的_.mergeWith()合并它们,并将重复项收集到数组中:

const a = {"first_name":"Tom","height":{"feet":5,"inches":0}}
const b = {"first_name":"Thomas","last_name":"Walsh","email":"tomwalsh@domain.com","height":{"feet":6,"inches":0}}
const c = {"email":"tomwalsh@sample.edu"}

const shouldCollect = (s) => _.negate(_.overSome([
  _.isUndefined,
  _.isObject,
  _.partial(_.eq, s)
]))


const mergeDupes = (...args) => _.mergeWith({}, ...args, (o, s) => {
  if(_.isArray(o)) return _.uniq([...o, s]);
  if(shouldCollect(s)(o)) return [o, s];
})

const result = mergeDupes(a, b, c)

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

如果要删除非重复属性,则可以通过递归_.transform()来清理对象:

const a = {"first_name":"Tom","height":{"feet":5,"inches":0}}
const b = {"first_name":"Thomas","last_name":"Walsh","email":"tomwalsh@domain.com","height":{"feet":6,"inches":0}}
const c = {"email":"tomwalsh@sample.edu"}

const shouldCollect = (s) => _.negate(_.overSome([
  _.isUndefined,
  _.isObject,
  _.partial(_.eq, s)
]))

const omitNonDuplicates = obj =>
  _.transform(obj, (a, v, k) => {
    if (_.isArray(v)) a[k] = v;
    else if (_.isObject(v)) {
      const clean = omitNonDuplicates(v);
      if(!_.isEmpty(clean)) a[k] = clean;
      return;
    }
  });
  
const mergeDupes = (...args) => omitNonDuplicates(_.mergeWith({}, ...args, (o, s) => {
  if(_.isArray(o)) return _.uniq([...o, s]);
  if(shouldCollect(s)(o)) return [o, s];
}))

const result = mergeDupes(a, b, c)

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

答案 1 :(得分:0)

我认为没有功能可以执行此操作,您必须迭代它们的属性。

const a = { first_name: 'Tom', height: {feet: 5, inches: 0 } };
const b = { first_name: 'Thomas', last_name: 'Walsh', email:'tomwalsh@domain.com',  height: { feet: 6, inches: 0} };
const c = { email: 'tomwalsh@sample.edu' };

let newObj = {};

[a, b, c].forEach(obj => {
    Object.entries(obj).forEach(([key, value]) => {
        if (newObj.hasOwnProperty(key)) { // if the property exist
            if (Array.isArray(newObj[key])) {
                newObj[key].push(value);
            } else {
                let temporalVal = newObj[key];
                newObj[key] = [temporalVal, value];
            }
        } else { // if the property does not exist, create it.
            newObj[key] = value;
        }
    })
});

console.log(newObj)

答案 2 :(得分:0)

您将需要一个递归函数,该函数将一并进入对象属性:

function mergeObjects(objs) {
    objs = objs.filter(obj => obj !== undefined);
    // return value or array when at least one of the values is a primitive
    if (objs.some(obj => Object(obj) !== obj)) { 
        objs = Array.from(new Set(objs)); // Get unique values
        return objs.length === 1 ? objs[0] : objs;
    }
    // Get all the keys
    const keys = Array.from(new Set([].concat(...objs.map(obj => Object.keys(obj))))); 
    return Object.assign({}, ...keys.map(key => 
        ({ [key]: mergeObjects(objs.map(obj => obj[key])) })));
}

// Demo:
const a = {first_name: 'Tom', height: {feet: 5,inches: 0}};
const b = {first_name: 'Thomas',last_name: 'Walsh',email: 'tomwalsh@domain.com', height: {feet: 6,inches: 0}};
const c = {email: 'tomwalsh@sample.edu'};

const result = mergeObjects([a,b, c]);
console.log(result);

答案 3 :(得分:0)

您可以通过检查值来采用递归方法,并在嵌套对象中获取内部值。

const
    merge = (a, b) => {
        Object.entries(b).forEach(([k, v]) => {
            if (v && typeof v === 'object') {
                return merge(a[k] = a[k] || {}, v);
            }
            if (!(k in a)) {
                return a[k] = v;
            }
            if ([].concat(a[k]).includes(v)) {
                return;
            }
            a[k] = [].concat(a[k], v); 
        });
        return a;
    },
    a = { first_name: 'Tom', height: { feet: 5, inches: 0 } },
    b = { first_name: 'Thomas', last_name: 'Walsh', email: 'tomwalsh@domain.com', height: { feet: 6, inches: 0 } },
    c = { email: 'tomwalsh@sample.edu', height: { feet: 15 } },
    result = [a, b, c].reduce(merge, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 4 :(得分:0)

这是通过Array.reduceArray.forEach的简洁递归解决方案:

const a = { first_name: 'Tom', height: { feet: 5, inches: 0 } },
      b = { first_name: 'Thomas', last_name: 'Walsh', email: 'tomwalsh@domain.com', height: { feet: 6, inches: 0 } },
      c = { email: 'tomwalsh@sample.edu'},
      d = { first_name: 'Tomy', height: { feet: 15, inches: 10 } }

const mergeKeys = (a,b) => {
  if(a != undefined) {
    if(a === b) return a
    if(typeof b == 'object') return merge([a,b])      
    else return [...(Array.isArray(a) ? a : [a]), b]
  } else return b
}

const merge = arr => arr.reduce((r,c) => 
  (Object.entries(c).forEach(([k,v]) => r[k] = mergeKeys(r[k],c[k])), r) ,{})

console.log(merge([a,b,c,d]))

答案 5 :(得分:0)

Lodash + Deepdash

 UPDATE staff
 SET commission = DECODE(position, 'Manager', 0.05, 0.04);

针对您的情况的完整test