Javascript 一个用于压平嵌套对象的衬垫

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33036487/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 14:33:10  来源:igfitidea点击:

One liner to flatten nested object

javascriptunderscore.js

提问by danday74

I need to flatten a nested object. Need a one liner. Not sure what the correct term for this process is. I can use pure Javascript or libraries, I particularly like underscore.

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

I've got ...

我有 ...

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

And I want ...

而且我要 ...

{
  a:2,
  c:3
}

I've tried ...

我试过了 ...

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

Which works but I also need this to work ...

哪个有效,但我也需要它来工作......

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

回答by

Here you go:

干得好:

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

Summary: recursively create an array of one-property objects, then combine them all with Object.assign.

总结:递归地创建一个单一属性对象的数组,然后将它们全部与Object.assign.

This uses ES6 features including Object.assignor the spread operator, but it should be easy enough to rewrite not to require them.

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

For those who don't care about the one-line craziness and would prefer to be able to actually read it (depending on your definition of readability):

对于那些不关心单行疯狂​​并且希望能够实际阅读它的人(取决于您对可读性的定义):

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

回答by Webber

Simplified readable example, no dependencies

简化的可读示例,无依赖

/**
 * 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
}

Working example: https://jsfiddle.net/webbertakken/jn613d8p/2/

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

回答by James Brierley

It's not quite a one liner, but here's a solution that doesn't require anything from ES6. It uses underscore's extendmethod, which could be swapped out for jQuery's.

它不是一个简单的衬垫,但这里有一个不需要 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;    
}

回答by pkfm

Here are vanilla solutions that work for arrays, primitives, regular expressions, functions, any number of nested object levels, and just about everything else I could throw at them. The first overwrites property values in the manner that you would expect from Object.assign.

以下是适用于数组、原语、正则表达式、函数、任意数量的嵌套对象级别以及我可以向它们抛出的几乎所有其他内容的普通解决方案。第一个以您期望的方式覆盖属性值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)

The second accumulates values into an array.

第二个将值累积到数组中。

((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)

Also please do not include code like this in production as it is terribly difficult to debug.

另外请不要在生产中包含这样的代码,因为调试非常困难。

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));
});

回答by Noob

I like this code because it's a bit easier to understand.

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

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, []));

Demo https://stackblitz.com/edit/typescript-flatter

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

For insinde a typescript class use:

对于 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;
  }

回答by Sk93

This is a function I've got in my common libraries for exactly this purpose. I believe I got this from a similar stackoverflow question, but cannot remember which (edit: Fastest way to flatten / un-flatten nested JSON objects- Thanks Yoshi!)

这是我在公共库中拥有的一个函数,正是为了这个目的。我相信我是从类似的 stackoverflow 问题中得到的,但不记得是哪个(编辑:展平/取消展平嵌套 JSON 对象的最快方法- 谢谢 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;
}

This can then be called as follows:

这可以被称为如下:

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

You can also append this function to the standard Javascript string class as follows:

您还可以将此函数附加到标准 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;
}

With which, you can do the following:

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

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

回答by Johannes

To flatten only the first levelof the object and merge duplicateobject keys into an array:

展平只有第一级对象和合并重复的对象键到一个数组:

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))

回答by muratgozel

Here is a true, crazy one-liner that flats the nested object recursively:

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

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]}), {})

Multiline version, explained:

多行版本,解释:

// $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]}
  ), {})

An example:

一个例子:

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'
}

Happy one-liner day!

单行快乐!

回答by Ikenna Anthony Okafor

Here goes, not thoroughly tested. Utilizes ES6 syntax too!!

在这里,没有经过彻底的测试。也使用 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);

回答by Hau Le

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))

Demo https://stackblitz.com/edit/typescript-flatten-json

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