skip to Main Content

I have a string and need to end up with an object that count all apples for every name, my struggle was to get separate match for each apple they took

In the part <@Viktor><@Kate>took:apple::apple: I want to get two matches instead of 1, and more if there would be more apples like <@Viktor><@Kate>took:apple::apple::apple::apple:

const message = 'I had :apple::apple::apple::apple: :apple:<@Viktor><@Kate>took:apple::apple: and later <@viktor> took again:apple:but not <@kate>';

const result {
     viktor: 3,
     kate: 2
}

used this to handle the problem, but it wasn`t accepted by the interviewer coz I used join&split later. The task was to solve this problem only with regexp.

function countGifts(text) {
    let result = {}
    let names = ['viktor', 'kate']
    names.forEach(name=>{
        let regex =  new RegExp(`(${name}).*?(:apple:)+`, "gi")
        result[name] = text.match(regex).join('').split(':apple:').length-1
    })
    return result
}
console.log(
    countGifts(message)
);

I`m new to regexp, and would appreciate alternative solutions and hints for articles with complex examples, coz all I found was pretty easy

2

Answers


  1. I’ll assume that if multiple name tags follow each other with maybe some separation text, but not :apple:, they will all get the apples that follow. I will also assume that if a name tag is duplicated in such a list, the duplication will be ignored. So <@Victor><@Victor> :apple: would assign one apple to Victor, not two.

    Here is one way to do that:

    function countGifts(text) {
        const result = {};
        let isCollecting = false;
        const names = new Set; // Those that will get apples
        for (const [_, name] of text.matchAll(/:apple:|<@(w+)>/g)) {
            if (name === undefined) { // Add apple to current names
                isCollecting = false;
                for (const key of names) {
                    result[key] = (result[key] || 0) + 1;
                }
            } else {
                if (!isCollecting) names.clear(); // Start new list
                isCollecting = true;
                names.add(name.toLowerCase());
            }
        }
        return result;
    }
    
    const message = 'I had :apple::apple::apple::apple: :apple:<@Viktor><@Kate>took:apple::apple: and later <@viktor> took again:apple:but not <@kate>';
    
    const result = countGifts(message);
    console.log(result);
    Login or Signup to reply.
  2. If you can use PCRE compatible regex (e.g. python with the regex library, or PHP) you could use this regex (using viktor as a sample name):

    (?:<@viktor>(?:<@)?+|G(?<!^))(?:(?!<@).)*?:apple:
    

    This will match:

    • (?:<@viktor>(?:<@)?+|G(?<!^)) : either
      • <@viktor><? : literally <@viktor>, followed by
      • (?:<@)?+ an optional start of a name tag; or
      • G(?<!^) : the end of the previous match (alternate use of G to match beginning of string disallowed by negative lookbehind)
    • (?:(?!<@).)*? : a minimal number of characters, none of which are at the start of a name tag (a tempered greedy token)
    • :apple: : literally :apple:

    Regex demo on regex101

    Python usage:

    import regex
    
    names = ['viktor', 'kate']
    message = 'I had :apple::apple::apple::apple: :apple:<@Viktor><@Kate>took:apple::apple: and later <@viktor> took again:apple:but not <@kate>'
    
    res = { name : len(regex.findall(fr'(?:<@{name}><?|G(?<!^))(?:(?!<@).)*?:apple:', message, regex.I)) for name in names }
    

    Output:

    {
      'viktor': 3, 
      'kate': 2
    }
    

    PHP usage:

    $names = ['viktor', 'kate'];
    $message = 'I had :apple::apple::apple::apple: :apple:<@Viktor><@Kate>took:apple::apple: and later <@viktor> took again:apple:but not <@kate>';
    
    $res = [];
    foreach ($names as $name) {
        preg_match_all("/(?:<@$name>(?:<@)?+|G(?<!^))(?:(?!<@).)*?:apple:/i", $message, $matches);
        $res[$name] = count($matches[0]);
    }
    print_r($res);
    

    Output:

    Array
    (
        [viktor] => 3
        [kate] => 2
    )
    

    Demo on 3v4l.org

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search