繁体   English   中英

递归循环遍历 object 的对象

[英]Recursively loop through objects of object

我正在尝试通过 object 将递归 function 写入 go 并根据 ID 返回项目。 我可以让它的第一部分工作,但我很难尝试以递归方式获得这个 function 并且可以使用一组新的眼睛。 代码如下。 当您运行代码片段时,您会得到一个包含 6 个项目的数组,这对于第一次迭代是我想要的,但是我如何使用适当的参数调用我的 function 来获取嵌套项目? 我的最终目标是将所有以“Cstm”开头的对象(也包括嵌套对象)添加到 tablesAndValues 数组中。 在此之后我试图 model 我的代码: Get all key values from multi level nested array JavaScript ,但这处理的是对象数组,而不是对象的 object 。 非常感谢我能得到的任何提示或提示。

JSFiddle: https://jsfiddle.net/xov49jLs/

 const response = { "data": { "Cstm_PF_ADG_URT_Disposition": { "child_welfare_placement_value": "" }, "Cstm_PF_ADG_URT_Demographics": { "school_grade": "family setting", "school_grade_code": "" }, "Cstm_Precert_Medical_Current_Meds": [ { "med_name": "med1", "dosage": "10mg", "frequency": "daily" }, { "med_name": "med2", "dosage": "20mg", "frequency": "daily" } ], "Cstm_PF_ADG_URT_Substance_Use": { "dimension1_comment": "dimension 1 - tab1", "Textbox1": "text - tab1" }, "Cstm_PF_ADG_Discharge_Note": { "prior_auth_no_comm": "auth no - tab2" }, "Cstm_PF_ADG_URT_Clinical_Plan": { "cca_cs_dhs_details": "details - tab2" }, "container": { "Cstm_PF_Name": { "first_name": "same text for textbox - footer", "last_name": "second textbox - footer" }, "Cstm_PF_ADG_URT_Demographics": { "new_field": "mapped demo - footer" }, "grid2": [ { "Cstm_PF_ADG_COMP_Diagnosis": { "diagnosis_label": "knee", "diagnosis_group_code": "leg" } }, { "Cstm_PF_ADG_COMP_Diagnosis": { "diagnosis_label": "ankle", "diagnosis_group_code": "leg" } } ] }, "submit": true } }; function getNamesAndValues(data, id) { const tablesAndValues = [], res = data; Object.entries(res).map(([key, value]) => { const newKey = key.split('_')[0].toLowerCase(); // console.log(newKey) // -> 'cstm' if (newKey === id) { tablesAndValues.push({ table: key, values: value }); } else { // I can log value and key and see what I want to push // to the tablesAndValues array, but I can't seem to get // how to push the nested items. // console.log(value); // console.log(key); // getNamesAndValues(value, key) } }); return tablesAndValues; } console.log(getNamesAndValues(response.data, 'cstm'));

您只需要使用 rest 运算符在 else 语句中调用 tablesAndValues 的 push 并将值和 id 作为参数传递

 const response = { "data": { "Cstm_PF_ADG_URT_Disposition": { "child_welfare_placement_value": "" }, "Cstm_PF_ADG_URT_Demographics": { "school_grade": "family setting", "school_grade_code": "" }, "Cstm_Precert_Medical_Current_Meds": [ { "med_name": "med1", "dosage": "10mg", "frequency": "daily" }, { "med_name": "med2", "dosage": "20mg", "frequency": "daily" } ], "Cstm_PF_ADG_URT_Substance_Use": { "dimension1_comment": "dimension 1 - tab1", "Textbox1": "text - tab1" }, "Cstm_PF_ADG_Discharge_Note": { "prior_auth_no_comm": "auth no - tab2" }, "Cstm_PF_ADG_URT_Clinical_Plan": { "cca_cs_dhs_details": "details - tab2" }, "container": { "Cstm_PF_Name": { "first_name": "same text for textbox - footer", "last_name": "second textbox - footer" }, "Cstm_PF_ADG_URT_Demographics": { "new_field": "mapped demo - footer" }, "grid2": [ { "Cstm_PF_ADG_COMP_Diagnosis": { "diagnosis_label": "knee", "diagnosis_group_code": "leg" } }, { "Cstm_PF_ADG_COMP_Diagnosis": { "diagnosis_label": "ankle", "diagnosis_group_code": "leg" } } ] }, "submit": true } }; function getNamesAndValues(data, id) { const tablesAndValues = [], res = data; Object.entries(res).map(([key, value]) => { const newKey = key.split('_')[0].toLowerCase(); // console.log(newKey) // -> 'cstm' if (newKey === id) { tablesAndValues.push({ table: key, values: value }); } else { // I can log value and key and see what I want to push // to the tablesAndValues array, but I can't seem to get // how to push the nested items. // console.log(value); // console.log(key); tablesAndValues.push(...getNamesAndValues(value, id)) } }); return tablesAndValues; } console.log(getNamesAndValues(response.data, 'cstm'));

或者以更短的方式

function getNamesAndValues2(data, id) {
    return Object.entries(data).reduce((arr, [key, value]) => {
        arr.push(
            ...(key.split('_')[0].toLowerCase() === id ? [{ table: key, values: value }] : getNamesAndValues(value, id))
        );
        return arr
    }, []);
}

要通过单次推送获得结果,可以在递归调用时将结果表传递给 function,但在第一次调用时默认为空表。 我还将.map更改为.forEach ,因为未使用返回值:

 const response = { "data": { "Cstm_PF_ADG_URT_Disposition": { "child_welfare_placement_value": "" }, "Cstm_PF_ADG_URT_Demographics": { "school_grade": "family setting", "school_grade_code": "" }, "Cstm_Precert_Medical_Current_Meds": [ { "med_name": "med1", "dosage": "10mg", "frequency": "daily" }, { "med_name": "med2", "dosage": "20mg", "frequency": "daily" } ], "Cstm_PF_ADG_URT_Substance_Use": { "dimension1_comment": "dimension 1 - tab1", "Textbox1": "text - tab1" }, "Cstm_PF_ADG_Discharge_Note": { "prior_auth_no_comm": "auth no - tab2" }, "Cstm_PF_ADG_URT_Clinical_Plan": { "cca_cs_dhs_details": "details - tab2" }, "container": { "Cstm_PF_Name": { "first_name": "same text for textbox - footer", "last_name": "second textbox - footer" }, "Cstm_PF_ADG_URT_Demographics": { "new_field": "mapped demo - footer" }, "grid2": [ { "Cstm_PF_ADG_COMP_Diagnosis": { "diagnosis_label": "knee", "diagnosis_group_code": "leg" } }, { "Cstm_PF_ADG_COMP_Diagnosis": { "diagnosis_label": "ankle", "diagnosis_group_code": "leg" } } ] }, "submit": true } }; function getNamesAndValues(data, id, tablesAndValues = []) { const res = data; Object.entries(res).forEach(([key, value]) => { const newKey = key.split('_')[0].toLowerCase(); if (newKey === id) { tablesAndValues.push({ table: key, values: value }); } else { getNamesAndValues( value, id, tablesAndValues); } }); return tablesAndValues; } console.log(getNamesAndValues(response.data, 'cstm'));

这是一个工作版本。 如果值是数组或 object,我会递归调用主 function。 每次还传入计数数组的当前 state。

function getNamesAndValues(data, id, tablesAndValues = []) {
  const res = data;
 
  Object.entries(res).map(([key, value]) => {
    const newKey = key.split('_')[0].toLowerCase();
    const item = res[key];

    if (newKey === id) {
      tablesAndValues.push({
        table: key,
        values: value
      });
    }
    
    if(Array.isArray(item)) {
        return item.map(el => getNamesAndValues(el, id, tablesAndValues));
    }

    if(typeof item === 'object') {
        return getNamesAndValues(item, id, tablesAndValues);
    }

  })

  return tablesAndValues;
}

console.log(getNamesAndValues(response.data, 'cstm'));

这是使用生成器的另一种方法 -

const keySearch = (t = [], q = "") =>
  filter(t, ([ k, _ ]) => String(k).startsWith(q))

const r = 
  Array.from
    ( keySearch(response, "Cstm")
    , ([ table, values ]) =>
        ({ table, values })
    )

console.log(r)
[
  {
    table: 'Cstm_PF_ADG_URT_Disposition',
    values: { child_welfare_placement_value: '' }
  },
  {
    table: 'Cstm_PF_ADG_URT_Demographics',
    values: { school_grade: 'family setting', school_grade_code: '' }
  },
  {
    table: 'Cstm_Precert_Medical_Current_Meds',
    values: [ [Object], [Object] ]
  },
  {
    table: 'Cstm_PF_ADG_URT_Substance_Use',
    values: {
      dimension1_comment: 'dimension 1 - tab1',
      Textbox1: 'text - tab1'
    }
  },
  {
    table: 'Cstm_PF_ADG_Discharge_Note',
    values: { prior_auth_no_comm: 'auth no - tab2' }
  },
  {
    table: 'Cstm_PF_ADG_URT_Clinical_Plan',
    values: { cca_cs_dhs_details: 'details - tab2' }
  },
  {
    table: 'Cstm_PF_Name',
    values: {
      first_name: 'same text for textbox - footer',
      last_name: 'second textbox - footer'
    }
  },
  {
    table: 'Cstm_PF_ADG_URT_Demographics',
    values: { new_field: 'mapped demo - footer' }
  },
  {
    table: 'Cstm_PF_ADG_COMP_Diagnosis',
    values: { diagnosis_label: 'knee', diagnosis_group_code: 'leg' }
  },
  {
    table: 'Cstm_PF_ADG_COMP_Diagnosis',
    values: { diagnosis_label: 'ankle', diagnosis_group_code: 'leg' }
  }
]

上面, keySearch只是filter的一个特化——

function* filter (t = [], test = v => v)
{ for (const v of traverse(t)){
    if (test(v))
      yield v
  }
}

这是traverse的专业化-

function* traverse (t = {})
{ if (Object(t) === t)
    for (const [ k, v ] of Object.entries(t))
      ( yield [ k, v ]
      , yield* traverse(v)
      )
}

展开下面的代码片段以在您的浏览器中验证结果 -

 function* traverse (t = {}) { if (Object(t) === t) for (const [ k, v ] of Object.entries(t)) ( yield [ k, v ], yield* traverse(v) ) } function* filter (t = [], test = v => v) { for (const v of traverse(t)){ if (test(v)) yield v } } const keySearch = (t = [], q = "") => filter(t, ([ k, _ ]) => String(k).startsWith(q)) const response = {"data":{"Cstm_PF_ADG_URT_Disposition":{"child_welfare_placement_value":""},"Cstm_PF_ADG_URT_Demographics":{"school_grade":"family setting","school_grade_code":""},"Cstm_Precert_Medical_Current_Meds":[{"med_name":"med1","dosage":"10mg","frequency":"daily"},{"med_name":"med2","dosage":"20mg","frequency":"daily"}],"Cstm_PF_ADG_URT_Substance_Use":{"dimension1_comment":"dimension 1 - tab1","Textbox1":"text - tab1"},"Cstm_PF_ADG_Discharge_Note":{"prior_auth_no_comm":"auth no - tab2"},"Cstm_PF_ADG_URT_Clinical_Plan":{"cca_cs_dhs_details":"details - tab2"},"container":{"Cstm_PF_Name":{"first_name":"same text for textbox - footer","last_name":"second textbox - footer"},"Cstm_PF_ADG_URT_Demographics":{"new_field":"mapped demo - footer"},"grid2":[{"Cstm_PF_ADG_COMP_Diagnosis":{"diagnosis_label":"knee","diagnosis_group_code":"leg"}},{"Cstm_PF_ADG_COMP_Diagnosis":{"diagnosis_label":"ankle","diagnosis_group_code":"leg"}}]},"submit":true}} const result = Array.from ( keySearch(response, "Cstm"), ([ table, values ]) => ({ table, values }) ) console.log(result)

一个相当优雅的递归答案可能如下所示:

 const getNamesAndValues = (obj) => Object (obj) === obj? Object.entries (obj).flatMap (([k, v]) => [... (k.toLowerCase ().startsWith ('cstm')? [{table: k, value: v}]: []), ... getNamesAndValues (v) ]): [] const response = {data: {Cstm_PF_ADG_URT_Disposition: {child_welfare_placement_value: ""}, Cstm_PF_ADG_URT_Demographics: {school_grade: "family setting", school_grade_code: ""}, Cstm_Precert_Medical_Current_Meds: [{med_name: "med1", dosage: "10mg", frequency: "daily"}, {med_name: "med2", dosage: "20mg", frequency: "daily"}], Cstm_PF_ADG_URT_Substance_Use: {dimension1_comment: "dimension 1 - tab1", Textbox1: "text - tab1"}, Cstm_PF_ADG_Discharge_Note: {prior_auth_no_comm: "auth no - tab2"}, Cstm_PF_ADG_URT_Clinical_Plan: {cca_cs_dhs_details: "details - tab2"}, container: {Cstm_PF_Name: {first_name: "same text for textbox - footer", last_name: "second textbox - footer"}, Cstm_PF_ADG_URT_Demographics: {new_field: "mapped demo - footer"}, grid2: [{Cstm_PF_ADG_COMP_Diagnosis: {diagnosis_label: "knee", diagnosis_group_code: "leg"}}, {Cstm_PF_ADG_COMP_Diagnosis: {diagnosis_label: "ankle", diagnosis_group_code: "leg"}}]}, submit: true}} console.log (getNamesAndValues (response))
 .as-console-wrapper {max-height: 100%;important: top: 0}

但这并不像我想的那么简单。 此代码将匹配搜索和搜索中使用的测试与 output 的格式混合在一起。 这意味着它是一个定制的 function,它比我想要的更易于理解且可重用性更低。

我更喜欢使用一些可重用的功能,分离出这个功能的三个特性。 因此,虽然以下涉及更多代码行,但我认为它更有意义:

 const findAllDeep = (pred) => (obj) => Object (obj) === obj? Object.entries (obj).flatMap (([k, v]) => [... (pred (k, v)? [[k, v]]: []), ... findAllDeep (pred) (v) ]): [] const makeSimpleObject = (name1, name2) => ([k, v]) => ({[name1]: k, [name2]: v}) const makeSimpleObjects = (name1, name2) => (xs) => xs.map (makeSimpleObject (name1, name2)) const cstmTest = k => k.toLowerCase ().startsWith ('cstm') const getNamesAndValues = (obj) => makeSimpleObjects ('table', 'values') (findAllDeep (cstmTest) (obj)) const response = {data: {Cstm_PF_ADG_URT_Disposition: {child_welfare_placement_value: ""}, Cstm_PF_ADG_URT_Demographics: {school_grade: "family setting", school_grade_code: ""}, Cstm_Precert_Medical_Current_Meds: [{med_name: "med1", dosage: "10mg", frequency: "daily"}, {med_name: "med2", dosage: "20mg", frequency: "daily"}], Cstm_PF_ADG_URT_Substance_Use: {dimension1_comment: "dimension 1 - tab1", Textbox1: "text - tab1"}, Cstm_PF_ADG_Discharge_Note: {prior_auth_no_comm: "auth no - tab2"}, Cstm_PF_ADG_URT_Clinical_Plan: {cca_cs_dhs_details: "details - tab2"}, container: {Cstm_PF_Name: {first_name: "same text for textbox - footer", last_name: "second textbox - footer"}, Cstm_PF_ADG_URT_Demographics: {new_field: "mapped demo - footer"}, grid2: [{Cstm_PF_ADG_COMP_Diagnosis: {diagnosis_label: "knee", diagnosis_group_code: "leg"}}, {Cstm_PF_ADG_COMP_Diagnosis: {diagnosis_label: "ankle", diagnosis_group_code: "leg"}}]}, submit: true}} console.log (findAllDeep (cstmTest) (response))
 .as-console-wrapper {max-height: 100%;important: top: 0}

这些都是可重用程度不同的辅助函数:

  • makeSimpleObject接受两个键名,比如'foo''bar'并返回一个 function ,它接受一个二元素数组,比如[10, 20]并返回一个匹配它们的 object ,比如{foo: 10, bar: 20}

  • makeSimpleObjects对两个元素的数组 arrays 执行相同的操作: makeSimpleObjects('foo', 'bar')([[8, 6], [7, 5], [30, 9]]) //=> [{foo: 8, bar: 6}, {foo: 7, bar: 5}, {foo: 30, bar: 9}]

  • cstmTest是一个简单的谓词,用于测试一个键是否以"cstm"开头(不区分大小写)。

  • findAllDeep接受一个谓词并返回一个 function ,它接受一个 object 并返回一个由两元素 arrays 组成的数组,其中包含与谓词匹配的任何项目的键/值对。 (谓词同时提供了键和值;在当前情况下,我们只需要键,但 function 似乎是明智的选择。

我们主要的 function, getNamesAndValues ,使用findAllDeep (cstmTest)找到匹配的值,然后makeSimpleObjects ('table', 'values')将结果转换为最终格式。

请注意, findAllDeepmakeSimpleObjectmakeSimpleObjects都是可能在其他地方有用的函数。 此处的自定义仅在cstmTestgetNamesAndValues的简短定义中。 我会认为这是一场胜利。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM