通过深层嵌套对象中的特定键查找所有值JavaScript

时间:2019-02-24 22:34:26

标签: javascript arrays function object ecmascript-6

如何在深层嵌套对象中按特定键查找所有值?

例如,如果我有一个像这样的对象:

const myObj = {
  id: 1,
  children: [
    {
      id: 2,
      children: [
        {
          id: 3
        }
      ]
    },
    {
      id: 4,
      children: [
        {
          id: 5,
          children: [
            {
              id: 6,
              children: [
                {
                  id: 7,
                }
              ]
            }
          ]
        }
      ]
    },
  ]
}

如何通过id的键来获取此obj所有嵌套中所有值的数组。

注意:children是一致的名称,id不会存在于children对象之外。

因此,从obj,我想产生一个像这样的数组:

const idArray = [1, 2, 3, 4, 5, 6, 7]

谢谢!

9 个答案:

答案 0 :(得分:3)

使用递归。

const myObj = {  id: 1,  children: [    {      id: 2,      children: [        {          id: 3        }      ]    },    {      id: 4,      children: [        {          id: 5,          children: [            {              id: 6,              children: [                {                  id: 7,                }              ]            }          ]        }      ]    },  ]},
    loop = (array, key, obj) => {
      if (!obj.children) return;
      obj.children.forEach(c => {
        if (c[key]) array.push(c[key]); // is not present, skip!
        loop(array, key, c);
      });
    },
    arr = myObj["id"] ? [myObj["id"]] : [];
    
loop(arr, "id", myObj);
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:3)

您可以创建一个通用的递归函数,该函数可与任何属性和任何对象一起使用。

这使用Object.entries()Object.keys()Array.reduce()Array.isArray()Array.map()Array.flat()

停止条件是传入的对象为空时

const myObj = {
  id: 1,
  anyProp: [{
    id: 2,
    thing: { a: 1, id: 10 },
    children: [{ id: 3 }]
  }, {
    id: 4,
    children: [{
      id: 5,
      children: [{
        id: 6,
        children: [{ id: 7 }]
      }]
    }]
  }]
};

const getValues = prop => obj => {
  if (!Object.keys(obj).length) { return []; }

  return Object.entries(obj).reduce((acc, [key, val]) => {
    if (key === prop) {
      acc.push(val);
    } else {
      acc.push(Array.isArray(val) ? val.map(getIds).flat() : getIds(val));
    }
    return acc.flat();
  }, []);
}

const getIds = getValues('id');

console.log(getIds(myObj));

答案 2 :(得分:2)

您可以使用Object.entries来创建递归函数,如下所示:

const myObj = {
  id: 1,
  children: [{
      id: 2,
      children: [{
        id: 3
      }]
    },
    {
      id: 4,
      children: [{
        id: 5,
        children: [{
          id: 6,
          children: [{
            id: 7,
          }]
        }]
      }]
    },
  ]
};

function findIds(obj) {
  const entries = Object.entries(obj);
  let result = entries.map(e => {
    if (e[0] == "children") {
      return e[1].map(child => findIds(child));
    } else {
      return e[1];
    }
  });
  function flatten(arr, flat = []) {
    for (let i = 0, length = arr.length; i < length; i++) {
      const value = arr[i];
      if (Array.isArray(value)) {
        flatten(value, flat);
      } else {
        flat.push(value);
      }
    }
    return flat;
  }
  return flatten(result);
}

var ids = findIds(myObj);
console.log(ids);

this answer中的平整功能

ES5语法:

var myObj = {
  id: 1,
  children: [{
      id: 2,
      children: [{
        id: 3
      }]
    },
    {
      id: 4,
      children: [{
        id: 5,
        children: [{
          id: 6,
          children: [{
            id: 7,
          }]
        }]
      }]
    },
  ]
};

function findIds(obj) {
  const entries = Object.entries(obj);
  let result = entries.map(function(e) {
    if (e[0] == "children") {
      return e[1].map(function(child) {
        return findIds(child)
      });
    } else {
      return e[1];
    }
  });
  function flatten(arr, flat = []) {
    for (let i = 0, length = arr.length; i < length; i++) {
      const value = arr[i];
      if (Array.isArray(value)) {
        flatten(value, flat);
      } else {
        flat.push(value);
      }
    }
    return flat;
  }
  return flatten(result);
}

var ids = findIds(myObj);
console.log(ids);

答案 3 :(得分:1)

您可以像这样创建一个递归函数:

idArray = []

function func(obj) {
  idArray.push(obj.id)
  if (!obj.children) {
    return
  }

  obj.children.forEach(child => func(child))
}

样本片段:

const myObj = {
  id: 1,
  children: [{
      id: 2,
      children: [{
        id: 3
      }]
    },
    {
      id: 4,
      children: [{
        id: 5,
        children: [{
          id: 6,
          children: [{
            id: 7,
          }]
        }]
      }]
    },
  ]
}

idArray = []

function func(obj) {
  idArray.push(obj.id)
  if (!obj.children) {
    return
  }

  obj.children.forEach(child => func(child))
}

func(myObj)
console.log(idArray)

答案 4 :(得分:1)

  

注意:children是一致的名称,id不会存在于外部   children对象。

     

因此,从obj,我想产生一个像这样的数组:

const idArray = [1, 2, 3, 4, 5, 6, 7]

鉴于问题不包含任何关于如何从输入派生输出的限制,并且输入是一致的,其中属性"id"的值是一个数字,并且定义了id属性仅在"children"属性中,除了对象中第一个"id"的情况外,输入的JavaScript纯对象可以使用JSON,{{1 }} JSON.stringify()匹配RegExp属性和该属性名称后的一个或多个数字字符,然后使用/"id":\d+/g将其映射到"id"上一个匹配项的数字部分{ {1}}并使用加法运算符.match()

将数组值转换为JavaScript数字

Regexp

\d+ +函数可用于将对象内每个const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]}; let res = JSON.stringify(myObject).match(/"id":\d+/g).map(m => +m.match(/\d+/)); console.log(res);属性名的值JSON.stringify()到数组中

replacer

由于要匹配的输入的属性值是数字,因此所有JavaScript对象都可以转换为字符串,并且.push() "id"可用于替换所有非数字字符结果字符串到数组,const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]}; const getPropValues = (o, prop) => (res => (JSON.stringify(o, (key, value) => (key === prop && res.push(value), value)), res))([]); let res = getPropValues(myObject, "id"); console.log(res);位数字到JavaScript数字

RegExp

答案 5 :(得分:1)

这有点晚了,但是对于其他任何人,这是一个干净的,通用的递归函数:

function findAllByKey(obj, keyToFind) {
  return Object.entries(obj)
    .reduce((acc, [key, value]) => (key === keyToFind)
      ? acc.concat(value)
      : (typeof value === 'object')
      ? acc.concat(findAllByKey(value, keyToFind))
      : acc
    , [])
}

// USAGE
findAllByKey(myObj, 'id')

答案 6 :(得分:1)

我发现史蒂夫的答案最适合我的需求,可以对此进行推断并创建通用的递归函数。就是说,在处理null和未定义的值时遇到了问题,因此我扩展了条件以适应这种情况。这种方法使用:

Array.reduce()-它使用一个累加器函数,该函数将值的值附加到结果数组上。还将每个对象分成其key:value对,使您可以执行以下步骤:

  1. 您找到钥匙了吗?如果是这样,请将其添加到数组中;
  2. 如果没有,是否找到了带有值的对象?如果是这样,则密钥可能在其中。通过在此对象上调用函数来继续挖掘并将结果附加到结果数组上;和
  3. 最后,如果这不是对象,则返回结果数组不变。

希望有帮助!

const myObj = {
  id: 1,
  children: [{
      id: 2,
      children: [{
        id: 3
      }]
    },
    {
      id: 4,
      children: [{
        id: 5,
        children: [{
          id: 6,
          children: [{
            id: 7,
          }]
        }]
      }]
    },
  ]
}

function findAllByKey(obj, keyToFind) {
  return Object.entries(obj)
    .reduce((acc, [key, value]) => (key === keyToFind)
      ? acc.concat(value)
      : (typeof value === 'object' && value)
      ? acc.concat(findAllByKey(value, keyToFind))
      : acc
    , []) || [];
}

const ids = findAllByKey(myObj, 'id');

console.log(ids)

答案 7 :(得分:0)

我们现在将object-scan用于很多数据处理需求。它使代码更易于维护,但是花了一点时间。这是您如何使用它来回答您的问题

const objectScan = require('object-scan');

const find = (data, needle) => objectScan([needle], { rtn: 'value' })(data);

const myObj = {
  id: 1,
  children: [{
    id: 2,
    children: [
      { id: 3 }
    ]
  }, {
    id: 4,
    children: [
      {
        id: 5,
        children: [
          {
            id: 6,
            children: [
              { id: 7 }
            ]
          }
        ]
      }
    ]
  }]
};

console.log(find(myObj, '**.id'));
// => [ 7, 6, 5, 4, 3, 2, 1 ]

答案 8 :(得分:0)

import {flattenDeep} from 'lodash';

/**
 * Extracts all values from an object (also nested objects)
 * into a single array
 *
 * @param obj
 * @returns
 *
 * @example
 * const test = {
 *  alpha: 'foo',
 *  beta: {
 *    gamma: 'bar',
 *    lambda: 'baz'
 *  }
 * }
 *
 * objectFlatten(test) // ['foo', 'bar', 'baz']
 */
export function objectFlatten(obj: {}) {
  const result = [];
  for (const prop in obj) {
    const value = obj[prop];
    if (typeof value === 'object') {
      result.push(objectFlatten(value));
    } else {
      result.push(value);
    }
  }
  return flattenDeep(result);
}