Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have a simple issue but it's been driving me nuts lately. I'm making a Chemistry program (Android app, to be more specific) and I have a method that separates elements that the user inputs. For example, if someone where to enter "FeZnKPb", it would be separated into "Fe", "Zn", "K", and "Pb".

To do so, I'm using a few nested loops and variables to control it. I have all the elements defined in the Chem[] array. The separated elements are then stored in an array called savedChem[]. It basically just loops through all the element constants (Chem[]) and copies the name and formula of those elements into savedChem[] if it matches the input.

Here is my code for this:

public void separateElements(String Input)
{
    boolean found = false;
    int start = 0;
    int end = 2;
    int length = Input.length();

    while(length >= end)
    {
        for(int x = 0; x < numChemicals; x++)
        {
            if((end + 0) > length)
            {
                end += 5;
                break;
            }
            if(Input.substring(start, end).equals(Chem[x].getFormula()))
            {
                savedChem[numSaved].setName(Chem[x].getName());
                savedChem[numSaved].setFormula(Chem[x].getFormula());
                numSaved++;
                start += 2;
                end += 2;
                found = true;
            }
            else
            {
                found = false;
            }
        }
        if(!found)
        {
            start += 2;
            end += 2;
        }
    }
}

My problem is that it only works with 2-character elements such as "Fe" or "Zn". I want it to also recognize others like "K". Another issue is that it sometimes skips some other elements. For example, if I were to input "FeZnHg" it would separate them into "Fe", "Zn", and "Hg". However, if I enter them in a different order like "ZnFeHg", it only detects "Zn" and "Hg" but not "Fe" for some reason.

What other ways am I able to solve this to get it to work correctly?

share|improve this question
1  
Is correct case sensitivity a constraint? Ie, will Mercury always be Hg, or might it show up as HG? –  Nathaniel Ford Jul 30 '12 at 22:37
    
Have you tried debugging your code step by step ? Still better use a regular expression. –  Bhaskar Jul 30 '12 at 22:46
    
@NathanielFord Yes, it will always be like that. I have another method for correcting that. –  Pkmmte Jul 30 '12 at 22:58
    
You can always do something like: savedChem[] = separateElements(input).toArray(new ChemicalElement[0]); if you want to update the savedChem array while using a method that returns some value. (Having the separateElements method return a value will make it much easier to test later on...). –  Mason Bryant Jul 30 '12 at 23:36

3 Answers 3

up vote 4 down vote accepted

Given that elements are always either an upper case character and one or two lower case characters (and two only in the case of some very short lived elements), you can just use a regex with some lookahead to split your input into short strings.

You can split your string up into element strings with the split method and some lookahead. Taking Nathaniel Ford's comments into account:

public enum ChemicalElement {
    F, Fe, Zn, K, Pb, Umm, //and so on...
}

public List<ChemicalElement> separateElements(String input) {
    String[] inputParts = input.split("(?=[A-Z]{1,1}[a-z]{0,2})");

    List<ChemicalElement> elementList = new LinkedList<ChemicalElement>();
    for (int i = 1; i < inputParts.length; i++) {
        String inputPart = inputParts[i];

        // note: throws IllegalArgumentException for unknown elements
        ChemicalElement element = ChemicalElement.valueOf(inputPart);
        if (null != element) {
            elementList.add(element);
        }

    }
    return elementList;
}

So a test input like this:

String input = "FeZnKPbUmmK";
List<ChemicalElement> elements = this.separateElements(input);

will give you the following list:

[Fe, Zn, K, Pb, Umm, K]

share|improve this answer
1  
I concur with this solution, assuming strings are capital-letter-delimited. I might also recommend putting your chemical elements into an enumeration and encapsulating the comparison code there, so that your function can return a Set<ChemicalElement> rather than modifying an array instance member. (which is not really what you want). –  Nathaniel Ford Jul 30 '12 at 22:58
    
@NathanielFord Well, I actually want it to modify an array instance member for other reasons since I'll also use other values of it. It's part of the reason why the method is void. I'll try what Mason suggested and see if it works, though. –  Pkmmte Jul 30 '12 at 23:30
    
Thanks, Mason! Your solution worked perfectly after modifying it a little. –  Pkmmte Jul 31 '12 at 0:40
    
@Pkmmte If the calling method needs to modify the instance member, then you should do it there with the returned set; it will make testing far easier. Further, you probably don't want to use an array ([]), but a List or Set; using those will allow you to leverage object orientation a lot more. –  Nathaniel Ford Jul 31 '12 at 0:40
    
for (int i = 1; i < inputParts.length; i++) can be replaced by (for String inputPart ; inputParts) gives you cleaner code ;-) –  Waza_Be Aug 3 '12 at 6:06
  1. Instead of predefining the end, make the end start + length of chemical name being tested. So you would compare Chem[x].getFormula().equals(Input.substring(start, start + Chem[x].getFormula().length())) Also, you would then advance start by Chem[x].getFormula().length(). I think you would also need to validate start + Chem[x].getFormula().length() is less than the total length, or you'll get String Index out of bounds exception.
  2. When you find a chemical name match, you need to break from your inner loop checking the chemical names, otherwise you keep comparing with later names, and might end up with found=false. So your bottom if statement erroneously advances by two characters. For example, if Fe comes before Zn in your list, it won't find Fe after it has found Zn and will skip it.
share|improve this answer

here is my humble suggestion...for future of your program i guess it will also be better in cases like when you have multiple O atoms... if that's needed anyway...what i would do...i would create a so called 2d matrix(uses more memory but less computational power) 26x26 where each index corresponds to a letter...so we know that element symbols are unique and at most 2 letters(I'm not sure about those experimental ones...sorry:D)...then you will scan the string one by one...lets say you encounter F and e.. this automatically suggests the index of 2d matrix...so you will look inside that index...if it is full(by some sign) then you will add Fe to your general list...but lets say after F you saw a K...then when you look to matrix you will see that its empty and you will conclude that it is Fluorine...anyway as i said more memory space less computation...your choice

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.