Best way to flatten JS object (keys and values) to a single depth array

前端 未结 10 791
梦毁少年i
梦毁少年i 2020-12-05 05:10

I have written this small function to get all keys and values of an object and store them into an array. The object might contain arrays as values...

Object {

相关标签:
10条回答
  • 2020-12-05 05:27

    This solution can handle deeply nested objects

    const isObject = o => o && typeof o === 'object' && !(o instanceof Date);
    
    const flattenObject = obj => Object.entries(obj).reduce((acc, [key, val]) => ({
      ...acc, ...(isObject(val) ? flattenObject(val) : { [key]: val })
    }), {});
    

    Remember that this function returns an empty object for strings, dates, numbers, etc.

    0 讨论(0)
  • 2020-12-05 05:30

    Generate an array of tuples (two-element arrays) of keys and values (which might themselves be arrays), then deep-flatten it.

    function flattenObject(obj) { 
          return flatten(Object.keys(obj).map(k => [toNumber(k), obj[k]]));
    }
    
    // Substitute your own favorite flattening algorithm.
    const flatten = a => Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
    
    // Convert to number, if you can.
    const toNumber = n => isNaN(+n) ? n : +n;
    
    console.log(flattenObject({a: [1, 2], b: 3, 0: [1, 2, 3, 4, 5]}));

    0 讨论(0)
  • 2020-12-05 05:36

    Flattening Object can be done using recursion as below :

    Sample Input

      let obj = {
        name: "test",
        address: {
        personal: "abc", 
        office: {
           building : 'random'
           street : 'some street',
        }
        }
    }
    

    Expected Output

    {
        name : "test",
        address_personal: "abc"
        address_office_building: "random"
        address_office_street: "some street"
    }
    
    
    

    My Solution

      function flattenObj(obj, parent, res = {}){
        for(let key in obj){
            let propName = parent ? parent + '_' + key : key;
            if(typeof obj[key] == 'object'){
                flattenObj(obj[key], propName, res);
            } else {
                res[propName] = obj[key];
            }
        }
        return res;
    }
    

    Hope it helps

    0 讨论(0)
  • 2020-12-05 05:38

    I wanted to flatten my deep object to one level depth. None of the above solutions worked for me.

    My input:

    {
        "user": {
            "key_value_map": {
                "CreatedDate": "123424",
                "Department": {
                    "Name": "XYZ"
                }
            }
        }
    }
    

    Expected output:

    {
        "user.key_value_map.CreatedDate": "123424",
        "user.key_value_map.Department.Name": "XYZ"
    }
    

    Code that worked for me:

    function flattenObject(ob) {
        var toReturn = {};
    
        for (var i in ob) {
            if (!ob.hasOwnProperty(i)) continue;
    
            if ((typeof ob[i]) == 'object' && ob[i] !== null) {
                var flatObject = flattenObject(ob[i]);
                for (var x in flatObject) {
                    if (!flatObject.hasOwnProperty(x)) continue;
    
                    toReturn[i + '.' + x] = flatObject[x];
                }
            } else {
                toReturn[i] = ob[i];
            }
        }
        return toReturn;
    }
    
    0 讨论(0)
  • 2020-12-05 05:40

    This answer is an improvement of @Muthukrishnan 's answer

    If you want to flatten an object deeply outputting the values into a one level deep object keyed with the path of the value in the previous object

    (eg: { foo: { bar: 'baz'} } => { 'foo.bar': 'baz' })

    Here is how you can effectively do it:

    /**
     * @param ob Object                 The object to flatten
     * @param prefix String (Optional)  The prefix to add before each key, also used for recursion
     **/
    function flattenObject(ob, prefix = false, result = null) {
      result = result || {};
    
      // Preserve empty objects and arrays, they are lost otherwise
      if (typeof ob === 'object' && ob !== null && Object.keys(ob).length === 0) {
        result[prefix] = Array.isArray(ob) ? [] : {};
        return result;
      }
    
      prefix = prefix ? prefix + '.' : '';
    
      for (const i in ob) {
        if (Object.prototype.hasOwnProperty.call(ob, i)) {
          if (typeof ob[i] === 'object' && ob[i] !== null) {
            // Recursion on deeper objects
            flattenObject(ob[i], prefix + i, result);
          } else {
            result[prefix + i] = ob[i];
          }
        }
      }
      return result;
    }
    
    /**
     * Bonus function to unflatten an object
     *
     * @param ob Object     The object to unflatten
     */
    function unflattenObject(ob) {
      const result = {};
      for (const i in ob) {
        if (Object.prototype.hasOwnProperty.call(ob, i)) {
          const keys = i.match(/^\.+[^.]*|[^.]*\.+$|(?:\.{2,}|[^.])+(?:\.+$)?/g); // Just a complicated regex to only match a single dot in the middle of the string
          keys.reduce((r, e, j) => {
            return r[e] || (r[e] = isNaN(Number(keys[j + 1])) ? (keys.length - 1 === j ? ob[i] : {}) : []);
          }, result);
        }
      }
      return result;
    }
    
    
    // TESTS
    const obj = {
      value: {
        foo: {
          bar: 'yes',
          so: {
            freakin: {
              nested: 'Wow',
            }
          }
        },
      },
      // Some edge cases to test
      test: [true, false, [null, undefined, 1]],
      not_lost: [], // Empty arrays should be preserved
      not_lost2: {}, // Empty objects should be preserved
      // Be careful with object having dots in the keys
      'I.like.dots..in.object.keys...': "... Please don't override me",
      I: {
        like: {
          'dots..in': {
            object: {
              'keys...': "You've been overwritten"
            }
          }
        }
      }
    };
    console.log(flattenObject(['I', {'am': 'an array'}]));
    let flat = flattenObject(obj);
    console.log(flat, unflattenObject(flat));

    There is an obvious problem that you could encounter with flattening this way if your object contains keys with dots, this is documented in the fiddle

    0 讨论(0)
  • 2020-12-05 05:41

    You could just concat all keys and values. (It does not solve the type casting to number for keys.)

    var object =  { 0: [1, 2, 3, 4] },
        result = Object.keys(object).reduce(function (r, k) {
            return r.concat(k, object[k]);
        }, []);
        
    console.log(result);

    0 讨论(0)
提交回复
热议问题