Search code examples
javascriptarraysregexparsingmapping

How does one parse best each item of an ingredient list and does create a new object based on each parsing result?


I have this list of ingredients I am trying to make a regex to look for 1 cup , or 1 tsp or 1 tablespoon and so on.....

I have made this regex but It doesn't work as well. I am trying separate ingredients from the measurements.

So with this string 1 Chopped Tomato it should take out the 1 as amount and output this:

const output = [
  {
    val: "Chopped Tomato",
    amount: "1",
  },

And with this string below it should be able to take out ½ tsp from ½ tsp fine salt and output this:

const output = [
  {
    val: "fine sea salt",
    amount: "½ tsp",
  },

These are the values I am using for the measurements:

    const measures = [
      "tbsp","tablespoon","tsp","teaspoon","oz","ounce","fl. oz","fluid ounce","cup","qt",
      "quart","pt","pint","gal","gallon","mL","ml","milliliter","g","grams","kg","kilogram","l","liter",
];

This is the input and regex I built

const Ingris = [
  "1 teaspoon heavy cream",
  "1 Chopped Tomato",
  "1/2 Cup yogurt",
  "1 packet pasta ",
  "2 ounces paprika",
]


const FilterFunction = (term) => {
  let data = []
  if (term) {
    const newData = Ingris.filter(({
      ingridients
    }) => {
      if (RegExp(term, "gim").exec(ingridients))
        return ingridients.filter(({
            val
          }) =>
          RegExp(term, "gim").exec(val)
        ).length;
    })
    data.push(newData)
  } else {
    data = []
  }
};
console.log(FilterFunction("cup"))

Desired Output:

const output = [
  {
    val: "Tomato",
    amount: "1 Chopped ",
  },
  {
    val: "yogurt",
    amount: "1/2 Cup",
  },
  {
    val: "1",
    amount: "packet pasta ",
  },
  {
    val: "fine sea salt",
    amount: "½ tsp",
  },
  {
    val: "heavy cream",
    amount: "1/2 teaspoon",
  },
  {
    val: "paprika",
    amount: "2 ounces",
  },
];

Solution

  • Here is something that worked when I added packet and ounces (plural)

    It handles

    • Just amounts like 1, 2, ¼, ½, ¾ and 1/2
    • Just words without amounts like "Ground meat"
    • Compound measures like "fluid ounces" in singular and plural
    • Action words like chopped or ground

    All handled by one and a half regex and one destructuring assignment

    const measures = [
      "tbsp", "tablespoon", "tsp", "teaspoon", "oz", "ounce", "ounces", "cup", "qt", "packet", "quart", "pt", "pint", "gal", "gallon", "mL", "ml", "milliliter", "g", "grams", "kg", "kilogram", "l", "liter", 
      "fl. oz", "fluid ounce", "fluid ounces" ]; // plural after singular!
    const action = ["chopped","ground"]  
    
    const compound = measures.filter(measure => measure.split(" ").length > 1); // extract compound words
    
    const amountRe =     /^(\d+\/\d+|¼|½|¾|\d|\d+)/; // amounts like 1, 1/2 etc
    const amountValueRe = /(\d+\/\d+|¼|½|¾|\d|\d+) ([\w.]+) (.*)/; // first part must be the same as amountRe
    
    const makeList = list => list.map(line => {
      if (!amountRe.test(line)) return { value: line }; // no amounts found
    
      // test for compound measures
      compound.forEach(cmp => line = line.replace(cmp, cmp.split(" ").join("_"))); // add underscores if found
      
      // destruct the match on amount plus value or amount of amount plus value
      let [, num, measure, what] = line.match(amountValueRe);
      
      if (action.includes(measure.toLowerCase())) { // test for chopped
        what = `${measure} ${what}`; // or add an action item to the object
        measure = "";
      }
      
      const obj = {}
      if (num) obj.amount = num;
      if (measure) obj.measure = measure.split("_").join(" ").trim(); // remove added underscores
      if (what) obj.value = what;
      return obj;
    });
    
    const Ingris = [
      "Chicken breast",
      "Ground ginger",
      "1 teaspoon heavy cream",
      "2 fluid ounces lemon juice",
      "1 Chopped Tomato",
      "1/2 Cup yogurt",
      "2 fl. oz paprika",
      "1 fluid ounce water",
      "½ packet pasta ",
      "2 ounces paprika"
    ];
    
    console.log(makeList(Ingris))