-3

I want a Javascript code which extract values from an input by a template

for example

template

Ahmed love eating {food} with {person}

input

Ahmed love eating Burger with Mohsen

I want the code to output the values as object

{
   food: "Burger",
   person: "Mohsen"
}

the code should be flexible to handle any template, read the variables and extract it from the input

I asked ChatGPT, it did his best to generate a functional code but was not flexible to handle more templates

the code which ChatGPT generated

const parser = (template, input) => {
    // Use a regular expression to extract the values from the input string
    const regex = /{(\w+)}/g;
    let match;
    const values = {};
    while ((match = regex.exec(template))) {
      const key = match[1];
      const startIndex = match.index;
      const endIndex = regex.lastIndex;
      const value = input
        .substring(startIndex - 1, endIndex)
        .replace(/[{}]/g, '')
        .trim();
      values[key] = value;
    }

    return values;
  };

It's okay to use a libraries or frameworks to achieve that goal, in case the algorithm will be very big

  • 2
    What you're doing is achievable with JS but requires a lot of forethought: what happens with the string "Ahmed love eating burger with fries with Mohsen"? What you're trying is to mimic some kind of language processing neutral network to extract data out of a sentence based on a template. – Terry Mar 11 '23 at 10:45
  • I did not say, there's extensions like "with fries" and so on.. anyway I posted the correct answer – Ahmed Mohsen Mar 13 '23 at 09:19
  • 1
    The point from @Terry is quite important. If you fill in the template with `{food: 'Burgers with fries', person: 'Mohsen'}`, you will get `"Ahmed love eating Burger with fries with Mohsen"`. And if you fill it using `{food: 'Burgers', person: 'fries with Mohsen'}`, you will also get `"Ahmed love eating Burger with fries with Mohsen"`. These are not extensions. They are simple string values. So this cannot be unambiguously solved in the general case. Perhaps you can add enough restrictions to the template and input to make it clear, but it's not as simple as you seem to think. – Scott Sauyet Mar 13 '23 at 13:46
  • @ScottSauyet Sorry I may was not clear in my question, there's no 2 words in any variable should be extracted, for example I want to support > "Ahmed love tennis" only, not "Ahmed love tennis and Padel" – Ahmed Mohsen Mar 13 '23 at 22:26

2 Answers2

2

Since you're not worried about ambiguities (see comments on the question), I think we can write this more simply by using named capture groups:

const unfill = (template, result, match = result .match (new RegExp (
  template .replace (/\{[^\}]+\}/g, (s) => `(?<${s .slice (1, -1)}>.+)`)
))) => match && match.groups

console .log (unfill (
  "Ahmed loves eating {food} with {person}", 
  "Ahmed loves eating Burger with Mohsen"
)) //=> {food: 'Burger', person: 'Mohsen'}

console .log (unfill (
  "Ahmed loves eating {food} with {person}", 
  "Ahmed hates eating Burger with Mohsen"
)) //=> null

Using a regular expression replace, we convert our template string into a regular expression like this:

const regex = /Ahmed loves eating (?<food>.+) with (?<person>.+)/

Then we call that regex on the input string. If it matches, we return its groups subobject. If not, we just return null. There is no error checking, and you might want to add some. For instance, this would blow up with the template "Ahmed loves eating {food} with {food}". And you probably want to escape the input. But this does represent the most simple answer to the problem I can come up with.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
0

Here's the function which extract the values from any template.

const parser = (input, template) => {
    template = template.replace(/[()]/g, '');
    input = input.replace(/[()]/g, '');
    const regex = new RegExp(template.replace(/\{(.+?)\}/g, '([^\\s]+)'));
    const match = input.match(regex);
    if (!match) {
      return null;
    }

    const variables = template.match(/\{(.+?)\}/g)?.map((v) => v.slice(1, -1));
    const values = match.slice(1);
    return variables?.reduce(
      (obj, variable, index) => ({
        ...obj,
        [variable]: values[index],
      }),
      {}
    );
  };
E_net4
  • 27,810
  • 13
  • 101
  • 139