-1

I not sure what's happening. The same function with same input return different results when using regexp library of golang.

package main

import (
    "fmt"
    "regexp"
)

type PaymentNetworkData struct {
    Regex string
    Name  string
}

var PAYMENT_NETWORKS = map[string]PaymentNetworkData{
    "Mastercard": {
        Regex: "^5[1-5][0-9]{14}|^(222[1-9]|22[3-9]\\d|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{12}$",
        Name:  "Mastercard",
    },
    "VisaMaster": {
        Regex: "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$",
        Name:  "VisaMaster",
    },
}

func resolvePaymentNetwork(cardIn string) string {
    payNet := "Unknown"
    for _, v := range PAYMENT_NETWORKS {
        regex := regexp.MustCompile(v.Regex)

        if regex.MatchString(cardIn) {
            payNet = v.Name
        }
    }
    return payNet
}

func main() {

    in := "5103901404433835"

    for i := 1; i < 100; i++ {
        payNet := resolvePaymentNetwork(in)
        fmt.Println("Payment Network is: ", payNet)
    }
}

Input: 5103901404433835

Regex:

Mastercard: ^5[1-5][0-9]{14}|^(222[1-9]|22[3-9]\\d|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{12}$
VisaMaster: ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$

Golang Output:

Payment Network is:  VisaMaster
Payment Network is:  Mastercard
Payment Network is:  Mastercard
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster

I tested the same code with NodeJS and in this case the result was always the same.

JS Output:

Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
Payment Network is:  VisaMaster
markalex
  • 8,623
  • 2
  • 7
  • 32
rogrp6
  • 47
  • 1
  • 5
  • 2
    Your Mastercard regex is wrong, you need `Regex: "^(?:5[1-5][0-9]{14}|(?:222[1-9]|22[3-9]\\d|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{12})$"`, in your regex, `^5[1-5][0-9]{14}` does not match the entire string. – Wiktor Stribiżew Apr 25 '23 at 08:00
  • @WiktorStribiżew, problem of the OP is not with regex itself, but but with the fact that his function returns different results for the same input. Though you comment of cause is correct. – markalex Apr 25 '23 at 08:07
  • @regor, could it be, that you problem is caused by combination of the following: 1. iteration through map doesn't guarantee sequence of elements, 2. Both regexes match supplied card number? – markalex Apr 25 '23 at 08:09
  • 1
    It is a very frequent regex issue when OP forgets to anchor all alternatives in the regex that should match an entire string. – Wiktor Stribiżew Apr 25 '23 at 08:13
  • @WiktorStribiżew, I understand. But please look into golang demo provided: it executes same function with the same input 10 times, and sometimes gets different output (I had to push run a couple time, but it's reproducible). Please reopen question. – markalex Apr 25 '23 at 08:17
  • It is because the regex is incorrect. No need to reopen. – Wiktor Stribiżew Apr 25 '23 at 08:25
  • @WiktorStribiżew, how incorrect regex may result in different outputs for the **same** input? – markalex Apr 25 '23 at 08:27
  • @markalex, in fact, you have answered the question yourself: `1. iteration through map doesn't guarantee sequence of elements, 2. Both regexes match supplied card number`. – Zeke Lu Apr 25 '23 at 09:03

1 Answers1

6

You code has a couple problems:

  1. use map for no apparent reason,
  2. both regexes match supplied card number.

These problems, and a fact that iteration through map is not guaranteed to produce same sequence, results in non-idempotent function.

Here is corrected code:

package main

import (
    "fmt"
    "regexp"
)

type PaymentNetworkData struct {
    Regex *regexp.Regexp
    Name  string
}

var PAYMENT_NETWORKS = [2]PaymentNetworkData{
    {
        Regex: regexp.MustCompile("^(?:5[1-5][0-9]{14}|(?:222[1-9]|22[3-9]\\d|2[3-6]\\d{2}|27[0-1]\\d|2720)[0-9]{12})$"),
        Name:  "Mastercard",
    },
    {
        Regex: regexp.MustCompile("^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$"),
        Name:  "VisaMaster",
    },
}

func resolvePaymentNetwork(cardIn string) string {
    for _, v := range PAYMENT_NETWORKS {
        if v.Regex.MatchString(cardIn) {
            return v.Name
        }
    }
    return "Unknown"
}

func main() {
    in := "5103901404433835"

    for i := 1; i < 100; i++ {
        payNet := resolvePaymentNetwork(in)
        fmt.Println("Payment Network is: ", payNet)
    }
}

It uses array instead of map to guarantee sequence.

Also, I've changed you structure to compile regexes only once.

It outputs Payment Network is: Mastercard every time.

Demo here.

Notice, it still uses same regexes (with correction recommended by @WiktorStribiżew in comments). They don't look very good, especially this part (?:4[0-9]{12}(?:[0-9]{3})? - it will match 13 digits too.
You'll better check expected formats for card numbers, and correct expressions accordingly.

markalex
  • 8,623
  • 2
  • 7
  • 32
  • This have so much sense, thanks!. The reason for I used a map is because in another part of my code I need to get the regex and the name configured for specific key in O(1), as example: `PAYMENT_NETWORKS["Mastercard"].Name`. In my code the of key the map is allways the same of the property 'Name' but this will not necesary always happen. I didn't know that iterating a map in golang doesn't always guarantee the same order, I found a way to iterate a map in order [here](https://stackoverflow.com/questions/18342784/how-to-iterate-through-a-map-in-golang-in-order) – rogrp6 Apr 25 '23 at 15:46
  • @regor, inline links are written the other way around: `[text](link)` – markalex Apr 25 '23 at 15:49
  • 1
    @regor, you haven't specified your need of map, so I provided best solution for problem at hand. But iterating through map with guaranteed sequence should work too. – markalex Apr 25 '23 at 15:52