一个衬垫来压平嵌套对象

时间:2015-10-09 10:56:47

标签: javascript underscore.js

我需要展平嵌套对象。需要一个班轮。不确定这个过程的正确用语是什么。 我可以使用纯Javascript或库,我特别喜欢下划线。

我已经......

{
  a:2,
  b: {
    c:3
  }
}

我想要......

{
  a:2,
  c:3
}

我试过......

var obj = {"fred":2,"jill":4,"obby":{"john":5}};
var resultObj = _.pick(obj, "fred")
alert(JSON.stringify(resultObj));

哪个有效,但我也需要这个......

var obj = {"fred":2,"jill":4,"obby":{"john":5}};
var resultObj = _.pick(obj, "john")
alert(JSON.stringify(resultObj));

13 个答案:

答案 0 :(得分:20)

你走了:

Object.assign({}, ...function _flatten(o) { return [].concat(...Object.keys(o).map(k => typeof o[k] === 'object' ? _flatten(o[k]) : ({[k]: o[k]})))}(yourObject))

摘要:递归地创建一个属性对象的数组,然后将它们全部与Object.assign组合。

这使用ES6功能,包括Object.assign或扩展运算符,但它应该很容易重写而不需要它们。

对于那些不关心单线疯狂而又愿意真正阅读它的人(取决于你对可读性的定义):

Object.assign(
  {}, 
  ...function _flatten(o) { 
    return [].concat(...Object.keys(o)
      .map(k => 
        typeof o[k] === 'object' ?
          _flatten(o[k]) : 
          ({[k]: o[k]})
      )
    );
  }(yourObject)
)

答案 1 :(得分:6)

简化的可读示例,没有依赖性

/**
 * Flatten a multidimensional object
 *
 * For example:
 *   flattenObject({ a: 1, b: { c: 2 } })
 * Returns:
 *   { a: 1, c: 2}
 */
export const flattenObject = (obj) => {
  const flattened = {}

  Object.keys(obj).forEach((key) => {
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      Object.assign(flattened, flattenObject(obj[key]))
    } else {
      flattened[key] = obj[key]
    }
  })

  return flattened
}

工作示例:https://jsfiddle.net/webbertakken/jn613d8p/2/

答案 2 :(得分:4)

这不是一个单一的班轮,但这里的解决方案不需要任何ES6。它使用下划线的extend方法,可以换成jQuery的。

function flatten(obj) {
    var flattenedObj = {};
    Object.keys(obj).forEach(function(key){
        if (typeof obj[key] === 'object') {
            $.extend(flattenedObj, flatten(obj[key]));
        } else {
            flattenedObj[key] = obj[key];
        }
    });
    return flattenedObj;    
}

答案 3 :(得分:3)

这是适用于数组,基元,正则表达式,函数,任何数量的嵌套对象级别以及几乎所有我可以扔给他们的东西的原始解决方案。第一种覆盖Object.assign中期望的属性值。

((o) => {
  return o !== Object(o) || Array.isArray(o) ? {}
    : Object.assign({}, ...function leaves(o) {
    return [].concat.apply([], Object.entries(o)
      .map(([k, v]) => {
        return (( !v || typeof v !== 'object'
            || !Object.keys(v).some(key => v.hasOwnProperty(key))
            || Array.isArray(v))
          ? {[k]: v}
          : leaves(v)
        );
      })
    );
  }(o))
})(o)

第二个将值累加到一个数组中。

((o) => {
  return o !== Object(o) || Array.isArray(o) ? {}
    : (function () {
      return Object.values((function leaves(o) {
        return [].concat.apply([], !o ? [] : Object.entries(o)
          .map(([k, v]) => {
            return (( !v || typeof v !== 'object'
                || !Object.keys(v).some(k => v.hasOwnProperty(k))
                || (Array.isArray(v) && !v.some(el => typeof el === 'object')))
              ? {[k]: v}
              : leaves(v)
            );
          })
        );
      }(o))).reduce((acc, cur) => {
        return ((key) => {
          acc[key] = !acc[key] ? [cur[key]]
            : new Array(...new Set(acc[key].concat([cur[key]])))
        })(Object.keys(cur)[0]) ? acc : acc
      }, {})
    })(o);
})(o)

也请不要在生产中包含这样的代码,因为它很难调试。

function leaves1(o) {
  return ((o) => {
    return o !== Object(o) || Array.isArray(o) ? {}
      : Object.assign({}, ...function leaves(o) {
      return [].concat.apply([], Object.entries(o)
        .map(([k, v]) => {
          return (( !v || typeof v !== 'object'
              || !Object.keys(v).some(key => v.hasOwnProperty(key))
              || Array.isArray(v))
            ? {[k]: v}
            : leaves(v)
          );
        })
      );
    }(o))
  })(o);
}

function leaves2(o) {
  return ((o) => {
    return o !== Object(o) || Array.isArray(o) ? {}
      : (function () {
        return Object.values((function leaves(o) {
          return [].concat.apply([], !o ? [] : Object.entries(o)
            .map(([k, v]) => {
              return (( !v || typeof v !== 'object'
                  || !Object.keys(v).some(k => v.hasOwnProperty(k))
                  || (Array.isArray(v) && !v.some(el => typeof el === 'object')))
                ? {[k]: v}
                : leaves(v)
              );
            })
          );
        }(o))).reduce((acc, cur) => {
          return ((key) => {
            acc[key] = !acc[key] ? [cur[key]]
              : new Array(...new Set(acc[key].concat([cur[key]])))
          })(Object.keys(cur)[0]) ? acc : acc
        }, {})
      })(o);
  })(o);
}

const obj = {
  l1k0: 'foo',
  l1k1: {
    l2k0: 'bar',
    l2k1: {
      l3k0: {},
      l3k1: null
    },
    l2k2: undefined
  },
  l1k2: 0,
  l2k3: {
    l3k2: true,
    l3k3: {
      l4k0: [1,2,3],
      l4k1: [4,5,'six', {7: 'eight'}],
      l4k2: {
        null: 'test',
        [{}]: 'obj',
        [Array.prototype.map]: Array.prototype.map,
        l5k3: ((o) => (typeof o === 'object'))(this.obj),
      }
    }
  },
  l1k4: '',
  l1k5: new RegExp(/[\s\t]+/g),
  l1k6: function(o) { return o.reduce((a,b) => a+b)},
  false: [],
}
const objs = [null, undefined, {}, [], ['non', 'empty'], 42, /[\s\t]+/g, obj];

objs.forEach(o => {
  console.log(leaves1(o));
});
objs.forEach(o => {
  console.log(leaves2(o));
});

答案 4 :(得分:2)

这是我在我的公共库中实现的功能。 我相信我是从类似的stackoverflow问题得到的,但是不记得哪个(编辑:Fastest way to flatten / un-flatten nested JSON objects - 谢谢Yoshi!)

function flatten(data) {
    var result = {};
    function recurse (cur, prop) {
        if (Object(cur) !== cur) {
            result[prop] = cur;
        } else if (Array.isArray(cur)) {
             for(var i=0, l=cur.length; i<l; i++)
                 recurse(cur[i], prop + "[" + i + "]");
            if (l == 0)
                result[prop] = [];
        } else {
            var isEmpty = true;
            for (var p in cur) {
                isEmpty = false;
                recurse(cur[p], prop ? prop+"."+p : p);
            }
            if (isEmpty && prop)
                result[prop] = {};
        }
    }
    recurse(data, "");
    return result;
}

然后可以按如下方式调用:

var myJSON = '{a:2, b:{c:3}}';
var myFlattenedJSON = flatten(myJSON);

您还可以将此函数附加到标准Javascript字符串类,如下所示:

String.prototype.flattenJSON = function() {
    var data = this;
    var result = {};
    function recurse (cur, prop) {
        if (Object(cur) !== cur) {
            result[prop] = cur;
        } else if (Array.isArray(cur)) {
             for(var i=0, l=cur.length; i<l; i++)
                 recurse(cur[i], prop + "[" + i + "]");
            if (l == 0)
                result[prop] = [];
        } else {
            var isEmpty = true;
            for (var p in cur) {
                isEmpty = false;
                recurse(cur[p], prop ? prop+"."+p : p);
            }
            if (isEmpty && prop)
                result[prop] = {};
        }
    }
    recurse(data, "");
    return result;
}

使用它,您可以执行以下操作:

var flattenedJSON = '{a:2, b:{c:3}}'.flattenJSON();

答案 5 :(得分:2)

仅展平对象的第一级,并将重复的对象对象键合并到数组中:

var myObj = {
  id: '123',
  props: {
    Name: 'Apple',
    Type: 'Fruit',
    Link: 'apple.com',
    id: '345'
  },
  moreprops: {
    id: "466"
  }
};

const flattenObject = (obj) => {
  let flat = {};
  for (const [key, value] of Object.entries(obj)) {
    if (typeof value === 'object' && value !== null) {
      for (const [subkey, subvalue] of Object.entries(value)) {
        // avoid overwriting duplicate keys: merge instead into array
        typeof flat[subkey] === 'undefined' ?
          flat[subkey] = subvalue :
          Array.isArray(flat[subkey]) ?
            flat[subkey].push(subvalue) :
            flat[subkey] = [flat[subkey], subvalue]
      }
    } else {
      flat = {...flat, ...{[key]: value}};
    }
  }
  return flat;
}

console.log(flattenObject(myObj))

答案 6 :(得分:2)

这是一个真实的,疯狂的单行代码,它递归地平铺嵌套的对象:

const flatten = (obj, roots = [], sep = '.') => Object.keys(obj).reduce((memo, prop) => Object.assign({}, memo, Object.prototype.toString.call(obj[prop]) === '[object Object]' ? flatten(obj[prop], roots.concat([prop])) : {[roots.concat([prop]).join(sep)]: obj[prop]}), {})

多行版本,说明:

// $roots keeps previous parent properties as they will be added as a prefix for each prop.
// $sep is just a preference if you want to seperate nested paths other than dot.
const flatten = (obj, roots = [], sep = '.') => Object
  // find props of given object
  .keys(obj)
  // return an object by iterating props
  .reduce((memo, prop) => Object.assign(
    // create a new object
    {},
    // include previously returned object
    memo,
    Object.prototype.toString.call(obj[prop]) === '[object Object]'
      // keep working if value is an object
      ? flatten(obj[prop], roots.concat([prop]))
      // include current prop and value and prefix prop with the roots
      : {[roots.concat([prop]).join(sep)]: obj[prop]}
  ), {})

一个例子:

const obj = {a: 1, b: 'b', d: {dd: 'Y'}, e: {f: {g: 'g'}}}
const flat = flatten(obj)
{
  'a': 1, 
  'b': 'b', 
  'd.dd': 'Y', 
  'e.f.g': 'g'
}

一线快乐!

答案 7 :(得分:1)

function flatten(obj: any) {
  return Object.keys(obj).reduce((acc, current) => {
    const key = `${current}`;
    const currentValue = obj[current];
    if (Array.isArray(currentValue) || Object(currentValue) === currentValue) {
      Object.assign(acc, flatten(currentValue));
    } else {
      acc[key] = currentValue;
    }
    return acc;
  }, {});
};

let obj = {
  a:2,
  b: {
    c:3
  }
}

console.log(flatten(obj))

演示 https://stackblitz.com/edit/typescript-flatten-json

答案 8 :(得分:1)

我喜欢这段代码,因为它更容易理解。

const data = {
  a: "a",
  b: {
    c: "c",
    d: {
      e: "e",
      f: [
        "g",
        {
          i: "i",
          j: {},
          k: []
        }
      ]
    }
  }
};

function flatten(data: any, response = {}, flatKey = "") {
  for (const [key, value] of Object.entries(data)) {
    const newFlatKey = `${flatKey}${key}`;
    if (typeof value === "object" && value !== null && Object.keys(value).length > 0) {
      flatten(value, response, `${newFlatKey}.`);
    } else {
      if (Array.isArray(response)) {
        response.push({
          [newFlatKey]: value
        });
      } else {
        response[newFlatKey] = value;
      }
    }
  }
  return response;
};

console.log(flatten(data));
console.log(flatten(data, []));

演示https://stackblitz.com/edit/typescript-flatter

对于insinde打字稿类,请使用:

  private flatten(data: any, response = {}, flatKey = ''): any {
    for (const [key, value] of Object.entries(data)) {
      const newFlatKey = `${flatKey}${key}`;
      if (typeof value === 'object' && value !== null && Object.keys(value).length > 0) {
        this.flatten(value, response, `${newFlatKey}.`);
      } else {
        if (Array.isArray(response)) {
          response.push({
            [newFlatKey]: value
          });
        } else {
          response[newFlatKey] = value;
        }
      }
    }
    return response;
  }

答案 9 :(得分:1)

这里,没有经过全面测试。也使用ES6语法!

loopValues(val){
let vals = Object.values(val);
let q = [];
vals.forEach(elm => {
  if(elm === null || elm === undefined) { return; }
    if (typeof elm === 'object') {
      q = [...q, ...this.loopValues(elm)];
    }
    return q.push(elm);
  });
  return q;
}

let flatValues = this.loopValues(object)
flatValues = flatValues.filter(elm => typeof elm !== 'object');
console.log(flatValues);

答案 10 :(得分:1)

我知道它已经很长了,但它可能对未来的某个人有所帮助

我使用过递归

let resObj = {};
function flattenObj(obj) {
    for (let key in obj) {
        if (!(typeof obj[key] == 'object')) {
            // console.log('not an object', key);
            resObj[key] = obj[key];
            // console.log('res obj is ', resObj);
        } else {
            flattenObj(obj[key]);
        }
    }

    return resObj;
}

答案 11 :(得分:1)

这是@Webber 的回答中我的 TypeScript 扩展。还支持日期:

private flattenObject(obj: any): any {
  const flattened = {};

  for (const key of Object.keys(obj)) {
    if (isNullOrUndefined(obj[key])) {
      continue;
    }

    if (typeof obj[key].getMonth === 'function') {
      flattened[key] = (obj[key] as Date).toISOString();
    } else if (typeof obj[key] === 'object' && obj[key] !== null) {
      Object.assign(flattened, this.flattenObject(obj[key]));
    } else {
      flattened[key] = obj[key];
    }
  }

  return flattened;
}

答案 12 :(得分:1)

我的 ES6 版本:

const flatten = (obj) => {
    let res = {};
    for (const [key, value] of Object.entries(obj)) {
        if (typeof value === 'object') {
            res = { ...res, ...flatten(value) };
        } else {
            res[key] = value;
        }
    }
    return res;
}