Need help with this slice algorithm

Say I have an array similar to this:

[
    '1 medium-sized Gruffalo',
    '8 pounds oats',
    '2 pounds brown sugar',
    '4 pounds flour',
    '2 gallons pure maple syrup',
    '16 cups chopped nuts',
    '1 pound baking soda',
    '1 pound baking powder',
    '1 pound cinnamon',
    '6 gallons melted butter',
    '2 gallons fresh water'
  ],

How would I return an array consisting of just the ingredients, WITHOUT USING SPLIT.

Example: ‘1 medium sized Gruffalo’ ------> ‘Gruffalo’

const stuffToStripOut = /\d+\s+(pounds*|gallons*|cups*|medium-sized)\s+/;
recipe.map(ingredient => ingredient.replace(stuffToStripOut, ""));

You have to either use a regex, or you write a function that goes through each string and removes/ignores everything that isn’t the thing you want. map takes an array, and applies a function to each element in the array, making a new array with the result: just writing it imperatively using a loop is equally valid.

The only way you can do this is if you know in advance the things you don’t want (you need a blacklist). So they are, in this instance (and I’ve used regex, so the relevant code is in brackets), from the start of each string:

  • a digit or digits (\d+, one or more digits)
  • a space or spaces (\s+, one or more spaces)
  • a quantity (pounds*|gallons*|cups*|medium-sized, “pound” + zero or more “s” OR “gallon” + zero or more “s” OR etc etc)
  • a space or spaces (\s+, one or more spaces)
1 Like

Alright thank you! I figured regex would be the way to go I just wasn’t sure if there was an easier way aka one that didn’t use regex :wink:

Thanks again

:+1: you can manually iterate through the string – for example maybe write a function that loops through the words in a string and dumps those that are [again] on some blacklist – but that is basically manually doing what a regex does, you’re kinda stuck with this because of the nature of the problem :upside_down_face:

1 Like

Would it be better to use ? instead of * to just make the s optional or are there not any edge cases where using the * would cause an issue?

const stuffToStripOut = /\d+\s+((pound|gallon|cup)s?|medium-sized)\s+/;

Any chance you know how to get everything after the second space? I’m thinking that’s my best option because I noticed the tests use a whole bunch of different quantities.

Maybe with this regex: /( ).*?( )/; ???

I think the following might work.

const stuffToStripOut = /(.+?\s+.+?\s+)/;

Thanks a lot! I was able to solve it with this

const listFoods = (recipe) => {
  // const pattern = / .*?( )/;
  const regex = /(.+?\s+.+?\s+)/;
  const result = recipe.ingredients.map(ing => {
    const string = ing.replace(regex, ' ');
    return string.trimLeft();
  });
  return result;
};

Have a good night

1 Like

Yes that would definitely be better. Probably some way to build the regex from a dictionary as well (then starting to get into natural language problems). This is completely untested and done on phone, so it’s likely to be slightly wrong, but maybe like:

imperial = [
  "cup",
  "gallon",
  "pound",
  ...etc
];

const metricMeasures = [
  "gram",
  "litre",
  "liter",
  ...etc
];

const metricPrefixes = [
  "milli",
  "deci",
  "kilo",
];

const metric = () =>
  metricPrefixes.flatMap(pre =>
    metricMeasures.map(m => `${pre}${m}`));

const measures = [...imperial, ...metric()].join("|");

const pattern = new RegExp`\\d+\\s+((${measures})s?)\\s+`;

It’s an interesting problem. Even there, it’s starting to get out of being simple enough for regex, it’s getting to the point where a parser would be simpler

It’ll work in this case, but not in the general case, consider, for quantity:

  • one, two, three etc
  • pinch of
  • handful of
  • bunch of
  • dash of
  • {number} ½
  • {number} and a half
  • a

And for measures, there could be many things that don’t naïvely fit, like “medium sized” (no hyphen).

Then you have options (“one large foomato or two small bazmatos”).

const ingredients = [
    '1 medium-sized Gruffalo',
    '8 pounds oats',
    '2 pounds brown sugar',
    '4 pounds flour',
    '2 gallons pure maple syrup',
    '16 cups chopped nuts',
    '1 pound baking soda',
    '1 pound baking powder',
    '1 pound cinnamon',
    '6 gallons melted butter',
    '2 gallons fresh water'
  ].map((reciepe) => reciepe.match(/(?<quantity>\d+)\s(?<amount_type>(pound|gallon|cup)s?|medium-sized)\s(?<ingredient>(.*))/).groups.ingredient);