views:

71

answers:

5

Hi all,

In a program I'm working on, i need the user to input time ranges, such as 2002-2003, or 1999- (implied present), or -2000 (before 2000).

I have an ArrayList of objects, and so the user might put it the title of books, including some information (one of these must be the date it was published). They can then search for a specific book, and time range is one of the ways to do it.

So i've taken in the date range they want as a String, and I have the date stored in my object as a integer. I know the cases, and I've tried to cover all the cases, but am having problems with the '-2010' and '2010-', and the '2010' cases.

The cases, as far as I can tell are: -2010 2010 2010-2011 2011-

could anyone suggest a basic algorithm, or perhaps outline some useful functions?

My idea was that by the end of the parsing, i should have a minimum and maximum date, such that i can look at a particular book, and ask, is the date it provided bigger than the minimum, and smaller than the maximum? But i still need to parse the ranges.

Thanks for the help everyone.

+1  A: 

Check if the first character is a -. Then check if the last character is a -. If it is neither of those cases, split at the - (use the String class's split method).

And check if the '-' actually exists in the string
Sam Dufel
this is a terribly incomplete and naive solution, it doesn't consider invalid input at all.
fuzzy lollipop
A: 

You can parse with the String.indexOf() function

int startDate, endDate, middleIndex;
middleIndex = inputString.indexOf("-");
if (middleIndex == -1)       //If there's no hyphen
  startDate = endDate = Integer.parseInt(inputString);
else if (middleIndex == 0) {  //if the hyphen is at the start of the string
  startDate = null;
  endDate = Integer.parseInt(inputString.substring(1, inputString.length));
}
else if (middleIndex == inputString.length - 1) {  //if the hyphen is at the end of the string
  startDate = Integer.parseInt(inputString.substring(0, inputString.length - 1));
  endDate = null;
}
else {  //if the hyphen separates 2 years
  startDate = Integer.parseInt(inputString.substring(0, middleIndex));
  endDate = Integer.parseInt(inputString.substring(middleIndex + 1, inputString.length));
}
Sam Dufel
I appreciate this answer! I didn't think of using the indexOf function, and using substring for parseInt.
Blackbinary
you don't do any validation or handle parse errors if there are illegal inputs
fuzzy lollipop
The question was about string parsing, not about sanitizing input. Yes, I know that input needs to be checked, but I don't feel the need to write validation code in every single code example I write.
Sam Dufel
A: 

As much as I usually abhor regular expressions this is a great place for using them. You get data validation as well as parsing all in one shot with the correct use of grouping.

^(\d{4})?(-)?(\d{4})?$ will give you 3 output groups, you check each one to see if you got a value and make decisions based on that, and as a bonus you are guaranteed that they are 4 digit numbers. Look at the groups on the Rubular page I link to. If you have a value in group 1 then that is the start year, a "-" in group two then it is a range, a value in group 3 an end range. Makes the logic really simple and you don't have to do all that silly string manipulation and Integer testing that other solutions suggest.

Since this is homework, here is something to kick start your thinking, how to use the Pattern and Matcher, I am not going to do the entire thing for you.

import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main
{
    public static void main(String[] args)
    {
        final List<String> inputs = Arrays.asList("1999-", "-2010", "1990-2000", "1967");

        final Pattern p = Pattern.compile("^(\\d{4})?(-?)(\\d{4})?$");
        int count = 0;
        for (final String input : inputs)
        {
            final Matcher m1 = p.matcher(input);
            m1.find();
            System.out.format("Input: %d\n", count++);
            for (int i=1; i <= m1.groupCount(); i++)
            {
                System.out.format("\tGroup %d = %s\n", i, m1.group(i));
            }
        }
    }
}

Here is the expected output:

Input: 0
 Group 1 = 1999
 Group 2 = -
 Group 3 = null
Input: 1
 Group 1 = null
 Group 2 = -
 Group 3 = 2010
Input: 2
 Group 1 = 1990
 Group 2 = -
 Group 3 = 2000
Input: 3
 Group 1 = 1967
 Group 2 = 
 Group 3 = null

How it works:

^ - start at the beginning of the line
(\d{4})? - this looks for exactly 4 digits \d and the () says group what you find, ? means this group is optional for a match, group will == null
(-)? - this looks for the dash, ? makes it optional, () groups the results at above
(\d{4})? - this looks for exactly 4 digits \d and the () says group what you find, ? means this group is optional for a match, group will == null
$ - matches the end of the line

Exercise left for the reader: The two cases it doesn't "validate" for is an empty string and "-", both of which should be considered "special" cases depending on what you want your business logic to be.

fuzzy lollipop
As neat as a solution like this is, I'm apt to avoid it for school assignments. I agree that in a situation like this, your method seems better.
Blackbinary
knowing how to correctly and appropriately use the tools that are built into the Java standard library is the most important thing you should be learning. Any of the other solutions used in a job interview question would certainly **not** get a you a call back.
fuzzy lollipop
A: 
  1. Use StringTokenizer to parse date(or simply use String#indexOf method)
  2. Use DateFormat to build relevant dates
  3. Use Date object's methods before/after for relevant comparisons to figure out the date range. http://download.oracle.com/javase/6/docs/api/
Ck-
It's not really necessary to create a Date object for this simple program. All he has is a 4-digit year, a full Date is overkill.
Sam Dufel
suggestion `StringTokenizer` is *BAD* practice, it is deprecated and for a reason, the recommended replacement is `Scanner`, and here a regular expression is a much better solution because of the extremely limited predefined sets of inputs.
fuzzy lollipop
@Sam Dufel - Date range? 1999 - 2010 @fuzzy lollipop - Yes regEx would be faster and simpler.
Ck-
A: 

Sorry, but if it is a "home work", previouse code surplus.

I only recommend use of Pattern and Matcher classes of java api.

angelcervera
`catch (Exception e)` and `throws Exception` is a very bad thing to show someone learning as an example!
fuzzy lollipop
catch (Exception e) is in unit test. It's a good practice always you use a "fail".Respect throw an Exception it is preferable create a specify Exception, but it is an example.
angelcervera
catching a SPECIFIC exception is good practice, catching `Exception` is __NEVER__ a good practice, especially in unit tests, it shows you are have not though the test out enough if you have exceptions that are not explicit expected. How does a student know its "just" an example, when it is wrong.
fuzzy lollipop
I wish there was more than `-1`, doing the home work for someone and doing it wrong should be `-10` at least.
fuzzy lollipop
Sorry but i do not pay attention to "home work" tag,In my answer, i provided an example using regexp, only a **fast** and altruist example, and never a evangelist example.I remember you and important point in http://stackoverflow.com/faq: Be nice.
angelcervera