Nesnenin nesneleri arasında özyinelemeli döngü

Aug 17 2020

Bir nesneden geçmek ve bir kimliğe göre öğeleri döndürmek için özyinelemeli bir işlev yazmaya çalışıyorum. Bunun ilk bölümünü çalıştırabilirim, ancak bu işlevi yinelemeli bir şekilde almaya çalışırken zorlanıyorum ve yeni bir dizi göz kullanabilirim. Kod aşağıdadır. Parçacığı çalıştırdığınızda, ilk yineleme için istediğim 6 öğelik bir dizi elde edersiniz, ancak iç içe geçmiş öğeleri elde etmek için işlevimi uygun parametrelerle nasıl çağırabilirim? Nihai hedefim, 'Cstm' ile başlayan tüm nesnelerin, iç içe olanların da tablesAndValues ​​dizisine eklenmesini sağlamaktır. Bundan sonra kodumu modellemeye çalışıyordum: Tüm anahtar değerleri çok seviyeli iç içe yerleştirilmiş JavaScript dizisinden alın , ancak bu bir nesneler dizisi ile ilgilenir, bir nesne nesnesi ile ilgilenmez. Alabileceğim herhangi bir ipucu veya ipucu çok takdir ediliyor.

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

Yanıtlar

1 traktor Aug 17 2020 at 07:30

Tek bir dokunuşla sonucu elde etmek için, sonuç tablosu özyinelemeli olarak çağrıldığında işleve geçirilebilir, ancak ilk çağrıda varsayılan olarak boş bir tabloya alınır. Dönüş değeri kullanılmadığı .mapiçin de değiştirdim .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

Sadece rest operatörü ile else ifadesinin içindeki tablolara ve değerlere push çağrısı yapmanız ve değeri ve kimliği parametre olarak iletmeniz yeterlidir.

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

Veya daha kısa bir şekilde

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

İşte çalışan bir versiyon. Değer bir dizi veya bir nesne ise özyinelemeli olarak ana işlevi çağırırım. Ayrıca her seferinde tallying dizisinin mevcut durumuna geçilir.

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

İşte jeneratörleri kullanan başka bir yaklaşım -

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

Yukarıda, keySearchsadece bir uzmanlık filter-

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

Hangi uzmanlık traverse-

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

Sonucu tarayıcınızda doğrulamak için aşağıdaki pasajı genişletin -

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

Oldukça zarif bir özyinelemeli cevap şöyle görünebilir:

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}

Ama bu istediğim kadar basit değil. Bu kod, eşleşmeleri aramayı ve bu aramada kullanılan testi çıktının biçimlendirmesiyle birlikte karıştırır. Bu, hem anlaşılması daha karmaşık hem de benim istediğimden daha az yeniden kullanılabilir olan özel bir işlev olduğu anlamına gelir.

Bu işlevselliğin üç özelliğini ayırarak bazı yeniden kullanılabilir işlevleri kullanmayı tercih ederim. Dolayısıyla, aşağıdakiler daha fazla kod satırı içeriyor olsa da, bence daha mantıklı:

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}

Bunların tümü, değişen derecelerde yeniden kullanılabilirliğe sahip yardımcı işlevlerdir:

  • makeSimpleObjectmesela, iki temel isimler alır 'foo've 'bar'bir iki öğeli bir dizi alan bir fonksiyon, ki döner [10, 20]gibi, bu eşleştirerek bir nesne ve döner{foo: 10, bar: 20}

  • makeSimpleObjectsİki eleman dizileri bir dizi için de aynı şey yapar: makeSimpleObjects('foo', 'bar')([[8, 6], [7, 5], [30, 9]]) //=> [{foo: 8, bar: 6}, {foo: 7, bar: 5}, {foo: 30, bar: 9}].

  • cstmTestbir anahtarın (büyük / küçük harf duyarlı) ile başlayıp başlamadığını test etmek için basit bir yüklemdir "cstm".

  • ve findAllDeepbir yüklemi alır ve bir nesneyi alan ve yüklemle eşleşen herhangi bir öğe için anahtar / değer çiftlerini tutan iki öğeli dizilerden oluşan bir dizi döndüren bir işlev döndürür. (Yüklem, hem anahtar hem de değer sağlanır; mevcut durumda yalnızca anahtara ihtiyacımız var, ancak işlevin ikisini de alması mantıklı görünüyor.

Ana işlevimiz, eşleşen değerleri bulmak ve ardından sonucu son biçime dönüştürmek için getNamesAndValueskullanır .findAllDeep (cstmTest)makeSimpleObjects ('table', 'values')

Unutmayın ki findAllDeep, makeSimpleObjectve makeSimpleObjectstüm işlevler muhtemelen başka yerlerde yararlı olabilir. Buradaki özelleştirme sadece için cstmTestve kısaca tanımlanmıştır getNamesAndValues. Bunu bir galibiyet olarak sayardım.