-1

I would need to find a way How to check if a given number is included in a string of numbers and ranges.

Here is an example: The string containing the numbers and ranges is "1,3,10-15,17" and the question could be e.g. "Is 13 included in the list?"

In this case the answer would be yes, as it is within the range 10-15. An example of a case where it wouldn't be could be e.g. number 16.

For my purposes it is enough to return something that can be translated to "yes" or "no".

Any help to solve this is appreciated.

Thanks

====Modified based on the answers received.====

The solution I ended up with is based on Java Stored procedure + PL/SQL Wrapper. Here is the code:

create or replace and compile java source named "RegExp" as
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegExp
{
  private static  Pattern P = Pattern.compile("(\\d+)-(\\d+)");

  public static int inRange (int p1, java.lang.String p2)
  {
    int iReturn = -1;
         int input;

         String[] sa;
         sa = p2.split(",");
         Set<Integer> is = new HashSet<Integer>();

         input = p1;

        for ( String s : sa)
        {
             Matcher m = P.matcher(s);
            if (m.find())
            {
                 int start = Integer.parseInt(m.group(1));
                 int end = Integer.parseInt(m.group(2));
                for (int i = start; i <= end; i++)
                {
                    is.add(i);
                }
            }
            else
            {
                is.add(Integer.parseInt(s));
            }
        }

        if (is.contains(input))
        {
           // System.out.format("Found %d in the set %s", input, is);
            iReturn = 1;
        }
        else
        {
           // System.out.format("Didn't find %d in set %s", input, is);
            iReturn = 0;
        }
        return iReturn;

  }

};

Here is the PL/SQL Wrapper

CREATE OR REPLACE FUNCTION InRange (p1  IN  number, p2  IN  VARCHAR2) RETURN NUMBER
AS LANGUAGE JAVA 
NAME 'RegExp.inRange (int, java.lang.String) return int';

And here is a sample SQL statement:

SELECT InRange(13,'1,3,10-15,17') FROM   DUAL;

This returns one (1) as number 13 is with the range 10-15

The following returns zero (0) as 6 is not in the list

SELECT InRange(6,'1,3,10-15,17') FROM   DUAL;

=== Update II ====
Although I'm not a Java programmer - so feel free to enhance the code - I did create a similar simple function to handle characters.

public static int inRangeString (java.lang.String p1, java.lang.String p2)
  {
      int iReturn = -1;


      String[] sa;
      sa = p2.split(",");

      Set<String> is = new HashSet<String>();


      for ( String s : sa)
        {
                 is.add(s);
        }

       if (is.contains(p1)) {
          iReturn = 1;  
              // value is valid  
          } else {  
                    iReturn = 0;
              // value is invalid  
          }  

       return iReturn;
  }
meteli
  • 23
  • 1
  • 7
  • 2
    Regular expressions alone are simply not smart enough to be able to solve this problem. You'd have to tell us what language / platform your using. Also, don't forget to show what you've tried so far! – p.s.w.g Jul 28 '14 at 21:35
  • 1
    *Some people, when confronted with a problem, think “I know, I'll use regular expressions.” Now they have two problems.* -- Jamie Zawinski –  Jul 28 '14 at 21:37
  • you can try a brute force regex that will match all patterns of numbers between those values – Sam I am says Reinstate Monica Jul 28 '14 at 21:37
  • Not actually a **Duplicate** because the valid range is an input, but is **Unclear**. My guess is that the user wants to take that list of ranges and convert it into a regex. – JasonMArcher Jul 28 '14 at 22:31

3 Answers3

1

Regular Expressions are part of the idiomatic solution.

But they are by no means the only part. Regular Expressions are for pattern matching, not for executing arbitrary logic, which you require.

Idiomatic Solution

import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;

public class Q25004732
{
    private static final Pattern RANGE = Pattern.compile("(?i)^(?=[a-z]+-[a-z]+$|\\d+-\\d+$)([a-z\\d]+)-([a-z\\d]+)$");

    public static void main(final String[] args)
    {
        final String[] sa = "1,3,10-15,17,A,b,XX-ZZ,z".split(",");
        final RangeSet<String> rs = TreeRangeSet.create();
        for (final String s : sa)
        {
            final Matcher m = RANGE.matcher(s);
            if (m.find())
            {
                rs.add(Range.closed(m.group(1), m.group(2)));
            }
            else
            {
                rs.add(Range.closed(s, s));
            }
        }
        report("13", rs);
        report("A", rs);
        report("XY", rs);
        report("c", rs);
        report("42", rs);
    }

    private static void report(@Nonnull final String input, @Nonnull final RangeSet<String> rs)
    {
        if (rs.contains(input))
        {
            System.out.format("Found %s in the set %s", input, rs);
        }
        else
        {
            System.out.format("Didn't find %s in set %s", input, rs);
        }
        System.out.println();
    }
}

Expected Output:

Found 13 in the set [[1‥1], [10‥15], [17‥17], [3‥3], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Found A in the set [[1‥1], [10‥15], [17‥17], [3‥3], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Found XY in the set [[1‥1], [10‥15], [17‥17], [3‥3], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Didn't find c in set [[1‥1], [10‥15], [17‥17], [3‥3], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Didn't find 42 in set [[1‥1], [10‥15], [17‥17], [3‥3], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]

Depends on:

Google Guava

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>[17.0,)</version>
</dependency>

FindBugs

<dependency>
    <groupId>com.google.code.findbugs</groupId>
    <artifactId>findbugs</artifactId>
    <version>[3.0.0,)</version>
</dependency>

Source code and other answers are available in my Stackoverflow GitHub repository.

  • Thanks, I think this answer helps me. Reason for this is that a) it corrects my understanding of regex - my hope was that regex could find if a value is between two ranges, so if 13 is included in the set 10-15 when the full string also has other values as in my example. B) a sample code showing how to possible get a solution - now i just need to translate/implement it in Oracle's, either as pure SQL or by programming a custom pl/sql function. ... – meteli Jul 30 '14 at 15:54
  • ... An attempt to make the question clearer this is all around SQL. I have a table with two columns COL1 and COL2. The table has two rows. In COL1 the values a A and B and in COL2 the matching strings are 1,3,10-15 and 6. I would like to have a SQL statement that would return "A" from COL1 if the where criteria is 13. In a mock SQL select r.* from R where REGEXP(col2, '') = '13' Hope this clarifies my question M – meteli Jul 30 '14 at 15:58
  • if you are trying to do this in `SQL` and not `Java` you need to tag your question appropriately! –  Jul 30 '14 at 17:25
  • The aim was to find a solution that uses REGEX which then could be translated to any language. One route I'm now exploring is to store your code as a Java function into the Oracle database and use it that way in SQL or PL/SQL. – meteli Jul 30 '14 at 17:28
  • First, Thank you for everyone who helped here and especially to Jarrod whoa solution is the bases in the following PL/SQL + Java implementation. – meteli Jul 31 '14 at 06:08
  • The implementation in PL/SQL + Java together with some sample SQL is included in the original question. – meteli Jul 31 '14 at 06:39
  • Jarrod, yes - it just took me a moment to find out how to mark the question answered and how to apply the "checkmark". But now all that is done I hope. I have also included the code into the question so it's easier to copy/paste if someone else has use for it. In my tests this works quite nice! – meteli Jul 31 '14 at 06:44
  • Jarrod, one more question - what would I need to change to make your solution also support characters in the same way as numbers? The characters could be any length, say between 1 and 5 characters long. An example could be is 'AD' included in group 'AA, AC-AF,Z' – meteli Jul 31 '14 at 07:17
  • @meteli - updated the answer to be more production worthy as well as handle numeric and character ranges with no giant list overhead. –  Jul 31 '14 at 19:00
1

The only way is to translate the range into a pattern (obviously), for that you need to forget that you are dealing with integers but only see digits as "normal characters". For your example range:

^(?:1[0-57]?|3)$

Note: regex is obviously not the way to check if an integer is in a numeric range. In real life, you will use good old conditionals.

Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
-1

This isn't possible to do with just a regex. What you can do is use a regex (though in this case that's overkill) to extract the numbers, separating them into two classes: numbers and ranges. Then you can use this to determine if a number is in the list.

lthreed
  • 444
  • 4
  • 11