Home > Back-end >  Sorting data for CSV export
Sorting data for CSV export

Time:07-25

I've been struggling with formatting an array of objects to fit a specific order for exporting in CSV.

I have the following array of ingredients:

const ingredients = [{
    name: 'Gluten Free Oats',
    retailer_id: 4,
    link: 'targetlink'
},
{
    name: 'Gluten Free Oats',
    retailer_id: 3,
    link: 'amazonlink'
},
{
    name: 'Adobo Seasoning',
    retailer_id: 50,
    link: 'walmartlink'
},
{
    name: 'Adobo Seasoning',
    retailer_id: 3,
    link: 'amazonlink'
},
{
    name: 'Adobo Seasoning',
    retailer_id: 4,
    link: 'targetlink'
},
{
    name: 'Adobo Seasoning',
    retailer_id: 135,
    link: 'krogerlink'
},
{
    name: 'America Cheese Spread',
    retailer_id: 3,
    link: 'amazonlink'
},
{
    name: 'Au Jus Gravy Mix',
    retailer_id: 68,
    link: 'wholefoodslink'
}]

I have the following array of retailer ids which correspond to retailer_id in the ingredients:

const order = [3, 4, 50, 68, 135]

This is supposed to represent an order of retailers as follows: Amazon (3), Target(4), Walmart(50), Whole Foods(68), Kroger(135).

How would I got about sorting the ingredients in the following format for a CSV file?

Ingredient Name,Amazon,Kroger,Target,Walmart,Whole Foods
Adobo Seasoning, amazonlink, krogerlink, targetlink, walmartlink, wholefoodslink
Gluten Free Oats, amazonlink, krogerlink, targetlink, walmartlink, wholefoodslink

The idea is to not have duplicate ingredient names and have their respective link per retailer "column" in the CSV. You can notice that the ingredient name appears multiple times in the ingredients array just with a different retailer_id. One ingredient can have multiple links per retailer (this can be enclosed in double quotes). If there's no match, then that link should be null.

CodePudding user response:

Assuming that each retailer_id from the ingredients is present in the order array, you could first create a new object that maps each ingredient by name to the ordered list of retailers:

const ingredientsWithLinks =
  ingredients.reduce((memo, entry) => {
    // If this is the first time we see that ingredient, initialize
    // an array of the length of our `order` list
    memo[entry.name] ||= Array(order.length);

    // Then, look up at which position the current entry's retailer
    // should be and place the link at that position within the array
    memo[entry.name][order.indexOf(entry.retailer_id)] = entry.link;

    // Because we're using `.reduce`, we need to return our new list
    // in each iteration
    return memo;
  }, {})

and after that, you could then translate this into a CSV format by simply reducing once again over that object and joining the items into a string:

Object
  .entries(ingredientsWithLinks)
  .reduce(
    (memo, entry) => `${memo}${entry[0]},${entry[1].join(",")}\n`,
    ""
  )

On your above example that would output something similar to:

Gluten Free Oats,amazonlink,targetlink,,,
Adobo Seasoning,amazonlink,targetlink,walmartlink,,krogerlink
America Cheese Spread,amazonlink,,,,
Au Jus Gravy Mix,,,,wholefoodslink,

So the only thing that's missing is your header. If you want to ensure a given order of the records, you could call .sort() on the in between the .entries(...) and .reduce(...) call and you'll get the list ordered by ingredient name.

  • Related