Home > Net >  How to create multiple objects from array of entries in JavaScript?
How to create multiple objects from array of entries in JavaScript?

Time:07-28

I receive an array of entries from form using FormData(). It consists of information about the recipe. Like this:

const dataArr = [
  ['title', 'pizza'],
  ['image', 'url'],
  ['quantity-0', '1'],
  ['unit-0', 'kg'],
  ['description-0', 'Flour'],
  ['quantity-1', '2'],
  ['unit-1', 'tbsp'],
  ['description-1', 'Olive oil'],
  ... // more ingredients
];

which I need to reorganize in new object, like this:

const recipe = {
  title: 'pizza',
  image: 'url',
  ingredients: [
    { quantity: '1', unit: 'kg', ingredient: 'Flour' },
    { quantity: '2', unit: 'tbsp', ingredient: 'Olive oil' },
    ...
  ],
};

So, for ingredients array I need to create multiple objects from received data. I came up with needed result, but it's not clean. I would appreciate your help coming up with universal function, when number of ingredients is unknown.

My solution: Form receives 6 ingredients max, therefore:

const ingredients = [];

// 1. Create an array with length of 6 (index helps to get ingredient-related data looping over the array)
const arrayOf6 = new Array(6).fill({});

arrayOf6.forEach((_, i) => {

  // 2. In each iteration filter over all data to get an array for each ingredient
  const ingArr = dataArr.filter(entry => {
    return entry[0].startsWith(`unit-${i}`) ||
    entry[0].startsWith(`quantity-${i}`) ||
    entry[0].startsWith(`ingredient-${i}`);
  });

  // 3. Loop over each ingredient array and rename future properties
  ingArr.forEach(entry => {
    [key, value] = entry;

    if(key.includes('ingredient')) entry[0] = 'description';
    if(key.includes('quantity')) entry[0] = 'quantity';
    if(key.includes('unit')) entry[0] = 'unit';
  });

  // 4. Transform array to object and push into ingredients array
  const ingObj = Object.fromEntries(ingArr);
  ingredients.push(ingObj);
});

// To finalize new object
const dataObj = Object.fromEntries(dataArr);
const recipe = {
  title: dataObj.title,
  image: dataObj.image,
  ingredients,
};

CodePudding user response:

You don't need arrayOf6. You never use its elements for anything -- it seems like you're just using it as a replacement for a loop like for (let i = 0; i < 6; i ).

Just loop over dataArr and check whether the name has a number at the end. If it does, use that as an index into the ingredients array, otherwise use the name as the property of the ingredients object. Then you don't need to hard-code a limit to the number of ingredients.

const dataArr = [
  ['title', 'pizza'],
  ['image', 'url'],
  ['quantity-0', '1'],
  ['unit-0', 'kg'],
  ['description-0', 'Flour'],
  ['quantity-1', '2'],
  ['unit-1', 'tbsp'],
  ['description-1', 'Olive oil'],
  // more ingredients
];

const recipe = {
  ingredients: []
};

dataArr.forEach(([name, value]) => {
  let match = name.match(/^(\w )-(\d )$/);
  if (match) {
    let type = match[1];
    let index = match[2];
    if (!recipe.ingredients[index]) {
      recipe.ingredients[index] = {};
    }
    recipe.ingredients[index][type] = value;
  } else {
    recipe[name] = value;
  }
});

console.log(recipe);

CodePudding user response:

Separating the key-parsing logic helps me think about the concerns more clearly:

const orderedKeyRegExp = /^(. )-(\d )$/;

function parseKey (key) {
  const match = key.match(orderedKeyRegExp);
  // Return -1 for the index if the key pattern isn't part of a numbered sequence
  if (!match) return {index: -1, name: key};
  return {
    index: Number(match[2]),
    name: match[1],
  };
}

function transformRecipeEntries (entries) {
  const result = {};
  const ingredients = [];

  for (const [key, value] of entries) {
    const {index, name} = parseKey(key);
    if (index >= 0) (ingredients[index] ??= {})[name] = value;
    //               ^^^^^^^^^^^^^^^^^^^^^^^^^
    // Assign an empty object to the element of the array at the index
    // (if it doesn't already exist)
    else result[name] = value;
  }

  if (ingredients.length > 0) result.ingredients = ingredients;
  return result;
}

const entries = [
  ['title', 'pizza'],
  ['image', 'url'],
  ['quantity-0', '1'],
  ['unit-0', 'kg'],
  ['description-0', 'Flour'],
  ['quantity-1', '2'],
  ['unit-1', 'tbsp'],
  ['description-1', 'Olive oil'],
  // ...more ingredients
];

const result = transformRecipeEntries(entries);
console.log(result);

CodePudding user response:

You'll have to parse the values of the input array to extract the index. To build the result object, you could use reduce:

const dataArr = [['title', 'pizza'],['image', 'url'],['quantity-0', '1'],['unit-0', 'kg'],['description-0', 'Flour'],['quantity-1', '2'],['unit-1', 'tbsp'], ['description-1', 'Olive oil']];

const recipe = dataArr.reduce((recipe, [name, value]) => {
  const [, prop, index] = name.match(/^(\w )-(\d )$/) ?? [];
  if (prop) {
    (recipe.ingredients[index] ??= {})[prop] = value;
  } else {
    recipe[name] = value;
  }
  return recipe;
}, { ingredients: [] });

console.log(recipe);

  • Related