Home > OS >  How does one detect and access numbered property names like 'entry1', 'entry2'?
How does one detect and access numbered property names like 'entry1', 'entry2'?

Time:01-12

A data structure similar to the following provided ...

{
  strIngredient1: 'Olive Oil',
  strIngredient2: 'Onion',
  strIngredient3: 'Chicken Breast',

  strMeasure1: '',
  strMeasure2: '',

  keyWithNoIndexCount: '',
}

... needs to be processed.

How could one use a loop in order to generate the number count within keys like strIngredient1, strIngredient2, ... and so on?

I am having trouble working out which terms to search for. I want to use a for loop so each number after strIngredient would be i.

I want to grab each entry like that ... strIngredient i ... and add them to an array but I do not know how to access them like this, if it is even possible or the best way. I do not know how to link the two parts of the following pseudo code ...

const ingredList = [];

for(i = 1; i < 20; i  ) {
  ingredList.push(response.meals[0].strMeasure ? ); 
  ingredList.push(response.meals[0].strIngredient ? ); 
}
console.log(ingredList);

Edit 1

There are also additional but non related entries in the data-structure.

Edit 2

There are also additional keys like strMeasure1, strMeasure2, strMeasure3 etc. I want to access them too in addition to the already mentioned strIngredient based ones.

CodePudding user response:

You can achieve that in two ways:

A: Grab values of all keys, regardless of key:

const jsonObj = {
  "strIngredient1": "Olive Oil",
  "strIngredient2": "Onion",
  "strIngredient3": "Chicken Breast"
};

let ingredListA = Object.keys(jsonObj).map(key => jsonObj[key]);
console.log('ingredListA:', ingredListA);

C: Grab values of by named & numbered keys:

const jsonObj = {
  "strIngredient1": "Olive Oil",
  "strIngredient2": "Onion",
  "strIngredient3": "Chicken Breast"
};

let ingredListB = [];
for(let i = 1; i <= 3; i  ) {
  let key = 'strIngredient'   i;
  ingredListB.push(jsonObj[key]);
}
console.log('ingredListB:', ingredListB);

If needed you could add a 3rd solution that is a hybrid of A and B: Loop through all object keys, use value only of key matches desired pattern.

CodePudding user response:

The OP needs a generic approach in order to parse from any given property-name the constant key part and the serial index. Like with a property name of 'strIngredient2' where the constant key is 'strIngredient' and the index is '2'.

One would achieve this via the usage of a regex like /(?<key>.*?\D)(?<index>\d*$)/ which features named capturing groups.

The regex-based parsing is part of a callback-function which does reduce the entries of the OP's provided object.

In order to stay even more generic by taking into account the renaming of some or all of the original object's property-names one would provide an object-based initial (reduce) value which features both,

  • a lookup for renaming some or all of the original property-names
  • and the result object which will feature any entry one would expect according to the OP's acceptance-criteria and the additional renaming.

function collectSerialEntry({ renaming, result }, [propName, value]) {
  const regXSerialKey = /(?<key>.*?\D)(?<idx>\d*$)/;

  let { key = null, idx = null } = regXSerialKey
    .exec(propName)
    ?.groups ?? {};

  if (key !== null) {
    key = renaming[key] ?? key;

    if (idx !== null) {
      idx = parseInt(idx, 10);
      idx = Number.isNaN(idx) && 1 || idx;

      // - create and/or access the `key` related array
      //   and assign the value according to the
      //   retrieved index.
      (result[key] ??= [])[idx - 1] = value;
    } else {
      // - assign the value according to its current key.^
      result[key] = value;
    }
  }
  return { renaming, result };
}


const sampleData = {
  strIngredient1: 'Olive Oil',
  strIngredient2: 'Onion',
  strIngredient3: 'Chicken Breast',
  strMeasure1: '100 ml',
  strMeasure2: '2',
  strMeasure3: '4',
  strDescription: 'Start frying ... lorem ipsum dolor sit amet.',
};


const {
  result: {
    ingredientList,
    measureList,
    description,
  },
} = Object
  .entries(sampleData)
  .reduce(collectSerialEntry, {
    // - provide an initial value ...
    renaming: {
      strIngredient: 'ingredientList',
      strMeasure: 'measureList',
      strDescription: 'description',
    },
    //   ... which features a lookup
    //   for renaming some or all of
    //   the original property-names ...
    //
    //   ...  and the `result` object
    //   which will feature any entry
    //   one would expect according to
    //   the OP's acceptance-criteria
    //   and the additional renaming.
    result: {},
  });

console.log(sampleData, ' => ', {
  ingredientList,
  measureList,
  description,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

  • Related