객체의 객체를 반복적으로 반복

Aug 17 2020

개체를 통과하고 ID를 기반으로 항목을 반환하는 재귀 함수를 작성하려고합니다. 이 작업의 첫 번째 부분을 작동시킬 수 있지만이 함수를 재귀적인 방식으로 가져 오는 데 어려움을 겪고 있으며 새로운 눈을 사용할 수 있습니다. 코드는 다음과 같습니다. 스 니펫을 실행하면 첫 번째 반복에서 원하는 항목 인 6 개 항목의 배열을 얻습니다. 그러나 중첩 된 항목을 가져 오기 위해 적절한 매개 변수를 사용하여 함수를 호출하려면 어떻게해야합니까? 내 최종 목표는 중첩 된 'Cstm'으로 시작하는 모든 개체를 tablesAndValues ​​배열에 추가하는 것입니다. 다음 코드를 모델링하려고했습니다. 다중 레벨 중첩 배열 JavaScript에서 모든 키 값 가져 오기 ,하지만 이것은 객체의 객체가 아닌 객체의 배열을 다룹니다. 제가 얻을 수있는 힌트 나 팁은 매우 감사합니다.

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

답변

1 traktor Aug 17 2020 at 07:30

단일 푸시로 결과를 얻으려면 재귀 적으로 호출 될 때 결과 테이블을 함수에 전달할 수 있지만 첫 번째 호출에서는 기본적으로 빈 테이블이됩니다. 반환 값이 사용되지 않기 때문에 다음과 같이 변경 .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'));

1 DavidNithaelTorresLima Aug 17 2020 at 07:18

나머지 연산자를 사용하여 else 문 내부의 tablesAndValues에 푸시를 호출하고 값과 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
    }, []);
}
1 JoeSeifi Aug 17 2020 at 07:49

다음은 작동하는 버전입니다. 값이 배열이거나 객체이면 주 함수를 재귀 적으로 호출합니다. 또한 매번 집계 배열의 현재 상태를 전달합니다.

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'));
1 Thankyou Aug 17 2020 at 08:02

생성기를 사용하는 또 다른 접근 방식이 있습니다.

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)

1 ScottSauyet Aug 18 2020 at 00:28

합리적으로 우아한 재귀 대답은 다음과 같습니다.

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}

그러나 이것은 내가 원하는만큼 간단하지 않습니다. 이 코드는 일치 검색 및 검색에 사용되는 테스트를 출력 형식과 함께 혼합합니다. 그것은 이해하기가 더 복잡하고 내가 원하는 것보다 재사용 가능성이 적은 사용자 정의 함수라는 것을 의미합니다.

이 기능의 세 가지 기능을 분리하여 몇 가지 재사용 가능한 기능을 사용하고 싶습니다. 따라서 다음은 더 많은 코드 라인을 포함하지만 더 합리적이라고 생각합니다.

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'그리고 두 소자 어레이를 취하는 함수라고 반환 [10, 20]등을 그와 일치하는 객체를 반환{foo: 10, bar: 20}

  • makeSimpleObjects요소가 두 개인 배열에 대해 동일한 작업을 수행합니다 makeSimpleObjects('foo', 'bar')([[8, 6], [7, 5], [30, 9]]) //=> [{foo: 8, bar: 6}, {foo: 7, bar: 5}, {foo: 30, bar: 9}]..

  • cstmTest키가로 시작하는지 (대소 문자를 구분하지 않음) 테스트하는 간단한 술어입니다 "cstm".

  • 그리고 findAllDeep술어를 취하고 객체를 취하고 술어와 일치하는 항목에 대한 키 / 값 쌍을 보유하는 두 요소 배열의 배열을 리턴하는 함수를 리턴합니다. (술어에는 키와 값이 모두 제공됩니다. 현재의 경우 키만 필요하지만 함수가 둘 중 하나를 취하는 것이 합리적입니다.

우리의 주 함수 인 getNamesAndValues은를 사용 findAllDeep (cstmTest)하여 일치하는 값을 찾은 다음 makeSimpleObjects ('table', 'values')결과를 최종 형식으로 변환합니다.

그 주 findAllDeep, makeSimpleObjectmakeSimpleObjects모든 기능을 가능성이 도움이 다른 곳에 있어야한다. 여기에서 사용자 cstmTest정의는 getNamesAndValues. 나는 그것을 승리로 간주 할 것이다.