Home > Mobile >  Pulling object elements from an array of objects and organizing them into an array
Pulling object elements from an array of objects and organizing them into an array

Time:01-04

After days of hairpulling, I decided to seek help:

  • I have an array of objects, one for each grocery store item. Each object has 3 properties: category (string for category of item), itemName (string for item itself), and onSale (boolean whether it's on sale or not):

    var itemData = [
    { category: 'fruit',  itemName: 'apple', onSale: false },
    { category: 'canned', itemName: 'beans', onSale: false },
    { category: 'canned', itemName: 'corn',  onSale: true  },
    { category: 'frozen', itemName: 'pizza', onSale: false },
    { category: 'fruit',  itemName: 'melon', onSale: true  },
    { category: 'canned', itemName: 'soup',  onSale: false },
    ];
    
  • I need to convert it into an object whose properties are the object categories. Each property is an array containing values in itemName that correspond to the property:

    {
    fruit:  ['apple', 'melon($)'],
    canned: ['beans', 'corn($)', 'soup'],
    frozen: ['pizza']
    };
    
  • When I try to do it, I end up with directionally correct values, except they're repeated:

    {
    fruit: [ 'apple', 'apple', 'melon', 'melon', 'melon' ],
    canned: [
      'beans', 'beans',
      'corn',  'corn',
      'corn',  'soup',
      'soup',  'soup'
    ],
    frozen: [ 'pizza', 'pizza' ]
    }
    
  • I spent hours trying to find out where I went wrong, to no avail. Here's my code:

    function organizeItems (array){
    //create output obj
    var output = {}
    //iterate over the array
    for(var i = 0; i < array.length; i  ){
    
    //iterate over the object in the array
      for(var category in array[i]){
        //if value of category isn't in output add the value to output as an empty array
        if(output[array[i]["category"]] === undefined){
          output[array[i]["category"]] = [];
          //if not --> push the itemName to the category
        } else{
          output[array[i]["category"]].push(array[i]["itemName"])
        }
        } 
      }
    return output
    }
    

How can I avoid the repetition and end up with the correct values in the arrays?

CodePudding user response:

You could group by category and adjust itemName.

const
    itemData = [{ category: 'fruit',  itemName: 'apple', onSale: false }, { category: 'canned', itemName: 'beans', onSale: false }, { category: 'canned', itemName: 'corn',  onSale: true  }, { category: 'frozen', itemName: 'pizza', onSale: false }, { category: 'fruit',  itemName: 'melon', onSale: true  }, { category: 'canned', itemName: 'soup',  onSale: false }],
    result = itemData.reduce((r, { category, itemName, onSale }) => {
        itemName  = onSale ? ' ($)' : '';
        (r[category] ??= []).push(itemName);
        return r;
    }, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

for(var category in array[i]){...

You don't have to iterate through the entries of each object as that will add unnecessary elements to the array like in your output 'melon', 'melon', 'melon'


You can use Array.prototype.reduce:

const itemData = [
{ category: 'fruit',  itemName: 'apple', onSale: false },
{ category: 'canned', itemName: 'beans', onSale: false },
{ category: 'canned', itemName: 'corn',  onSale: true  },
{ category: 'frozen', itemName: 'pizza', onSale: false },
{ category: 'fruit',  itemName: 'melon', onSale: true  },
{ category: 'canned', itemName: 'soup',  onSale: false },
];

const object = itemData.reduce((acc,el) => (acc[el.category] ??= [], acc[el.category].push(el.itemName   (el.onSale ? '($)' : '')), acc), {})

console.log(object)

CodePudding user response:

  1. Utilize Array.prototype.reduce

    • make use of an initial value (as shown with the one line above linked example code).
    • for the OP's use case the initial value too is an empty object (literal) ... {} ... which inside the reducer function is referred to as result. The latter, while iterating the array, gets build/accumulated programmatically with each new category and/or itemName.
  2. Destructure (each) itemData's array item.

  3. (Create and/or) Access the category specific array by using the Logical nullish assignment ... ??=

  4. push the correct (depending on onSale state) form of a category item; for instance by utilizing a Template literal

var itemData = [
  { category: 'fruit',  itemName: 'apple', onSale: false },
  { category: 'canned', itemName: 'beans', onSale: false },
  { category: 'canned', itemName: 'corn',  onSale: true  },
  { category: 'frozen', itemName: 'pizza', onSale: false },
  { category: 'fruit',  itemName: 'melon', onSale: true  },
  { category: 'canned', itemName: 'soup',  onSale: false },
];

// // expected result ...
// {
//   fruit:  ['apple', 'melon($)'],
//   canned: ['beans', 'corn($)', 'soup'],
//   frozen: ['pizza']
// };

console.log(
  itemData.reduce((result, item) => {

    // destructure `itemData`'s array `item`.
    const { category, itemName, onSale } = item;

    // (create and/or) access the `category` specific array.
    const categoryArray = (result[category] ??= []);

    // push the correct (depending on `onSale` state) form of a category item.
    categoryArray.push(`${ itemName }${ (onSale === true) ? '($)' : '' }`);

    return result;
  }, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

  •  Tags:  
  • Related