6

I have a String that's formatted like this:

"key1=value1;key2=value2;key3=value3"

for any number of key/value pairs.

I need to check that a certain key exists (let's say it's called "specialkey"). If it does, I want the value associated with it. If there are multiple "specialkey"s set, I only want the first one.

Right now, I'm looking for the index of "specialkey". I take a substring starting at that index, then look for the index of the first = character. Then I look for the index of the first ; character. The substring between those two indices gives me the value associated with "specialkey".

This is not an elegant solution, and it's really bothering me. What's an elegant way of finding the value that corresponds with "specialkey"?

user438293456
  • 596
  • 3
  • 6
  • 19
  • Please note that some of the Answer on this post was merged from [Extracting values corresponding to a certain "key" from a string List in Java](https://stackoverflow.com/questions/66174783/extracting-values-corresponding-to-a-certain-key-from-a-string-list-in-java?noredirect=1) which had specific keys and values as `Arrays.asList("A=1,B=2,C=3","A=11,B=12,C=13,D=15",...` – Scratte Feb 12 '21 at 20:30

9 Answers9

12

I would parse the String into a map and then just check for the key:

String rawValues = "key1=value1;key2=value2;key3=value3";
Map<String,String> map = new HashMap<String,String>();
String[] entries = rawValues.split(";");
for (String entry : entries) {
  String[] keyValue = entry.split("=");
  map.put(keyValue[0],keyValue[1]);
}

if (map.containsKey("myKey")) {
   return map.get("myKey");
}
Nick is tired
  • 6,860
  • 20
  • 39
  • 51
Mike Sickler
  • 33,662
  • 21
  • 64
  • 90
9

Use String.split:

String[] kvPairs = "key1=value1;key2=value2;key3=value3".split(";");

This will give you an array kvPairs that contains these elements:

key1=value1
key2=value2
key3=value3

Iterate over these and split them, too:

for(String kvPair: kvPairs) {
   String[] kv = kvPair.split("=");
   String key = kv[0];
   String value = kv[1];

   // Now do with key whatever you want with key and value...
   if(key.equals("specialkey")) {
       // Do something with value if the key is "specialvalue"...
   }
}
icyrock.com
  • 27,952
  • 4
  • 66
  • 85
5

If it's just the one key you're after, you could use regex \bspecialkey=([^;]+)(;|$) and extract capturing group 1:

Pattern p = Pattern.compile("\\bspecialkey=([^;]+)(;|$)");
Matcher m = p.matcher("key1=value1;key2=value2;key3=value3");

if (m.find()) {
    System.out.println(m.group(1));
}

If you're doing something with the other keys, then split on ; and then = within a loop - no need for regex.

mathematical.coffee
  • 55,977
  • 11
  • 154
  • 194
  • Notice how the string.split answers are not only obtuse, they begin to break down at the first hint of complexity in your input (say you need to match multiple keys at one time, or a key without a certain substring in it). It's also a nightmare to maintain or change, while this one is a breeze. – Scott Weaver Mar 13 '12 at 02:45
2

Just in case anyone is interested in a pure Regex-based approach, the following snippet works.

Pattern pattern = Pattern.compile("([\\w]+)?=([\\w]+)?;?");
Matcher matcher = pattern.matcher("key1=value1;key2=value2;key3=value3");
while (matcher.find()) {
   System.out.println("Key - " + matcher.group(1) + " Value - " + matcher.group(2);
}

Output will be

Key - key1 Value - value1
Key - key2 Value - value2
Key - key3 Value - value3

However, as others explained before, String.split() is recommended any day for this sort of task. You shouldn't complicate your life trying to use Regex when there's an alternative to use.

asgs
  • 3,928
  • 6
  • 39
  • 54
1

There are many ways to do this. Perhaps the simplest is to use the Streams API (available as of Java 8 and later) to process the match results:

List<String> OriginalList = Arrays.asList("A=1,B=2,C=3",
        "A=11,B=12,C=13,D=15", "A=5,B=4,C=9,D=10,E=13",
        "A=19,B=20,C=91,D=40,E=33", "A=77,B=27,C=37");
  • this streams the strings
  • matches on the pattern and extracts the integer
  • the collects to a list
Pattern p = Pattern.compile("A=(\\d+)");
List<Integer> result = OriginalList.stream().
        flatMap(str->p.matcher(str).results())
        .map(mr->Integer.valueOf(mr.group(1)))
        .collect(Collectors.toList());

System.out.println(result);

Prints:

[1, 11, 5, 19, 77]
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
WJS
  • 36,363
  • 4
  • 24
  • 39
0

Try : (?:(?:A=)([^,]*))

Demo : https://regex101.com/r/rziGDz/1

Else you find a code using regex and your list to get answer :

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.*;
    
public class Main 
{
    public static void main(String[] args) 
    {
      
    List<Integer> results = new ArrayList();
    Pattern pattern = Pattern.compile("(?:(?:A=)([^,]*))", Pattern.CASE_INSENSITIVE);
    List<String> OriginalList = Arrays.asList(
            "A=1,B=2,C=3",
            "A=11,B=12,C=13,D=15",
            "A=5,B=4,C=9,D=10,E=13",
            "A=19,B=20,C=91,D=40,E=33",
            "A=77,B=27,C=37");
            
        for (int i = 0; i < OriginalList.size(); i++) 
        {
            Matcher matcher = pattern.matcher(OriginalList.get(i));
            boolean matchFound = matcher.find();
            if(matchFound) 
            {
                System.out.println( matcher.group(1) );
                results.add( Integer.parseInt(matcher.group(1)) );
            }
        }
    }
}
0

Using basic filter: Split using [A-Z=,]+ regex. Pick the 2nd element.

public List filter() {
        List<String> originalList = Arrays.asList("A=1,B=2,C=3", "A=11,B=12,C=13,D=15", "A=5,B=4,C=9,D=10,E=13",
                "A=19,B=20,C=91,D=40,E=33", "A=77,B=27,C=37");
        List<Integer> parsedData = new ArrayList();
        
        for(String str: originalList) {
            Integer data = Integer.parseInt(str.split("[A-Z=,]+")[1]);
            parsedData.add(data);
            
        }
        return parsedData;
        
}
Md Kawser Habib
  • 1,966
  • 2
  • 10
  • 25
  • You can remove the `^` from your character class. It only works to either negate the entire class, when it's placed as the first character in the class, or it's treated as just another character. At present the string is split at any of: `=`, `^` or `,`. Had it not split at `,` the second element in the resulting String array would have been `1,B` which would result in a `java.lang.NumberFormatException` when trying to parse it into an `Integer`. You can check that I'm right with `System.out.println(Arrays.toString(str.split("[=^,]")));` – Scratte Feb 12 '21 at 19:08
  • str.split("[=^,]") returns an array of string, like s[0]=A, s[1]=1, s[2]=B ... . From, this array of string, I pick the value of index 1 and parse it to integer). In the github repo, I wrote more details -https://github.com/khabib97/StackOverflow-Responses/blob/main/StringParseRegEx/FilterString.java – Md Kawser Habib Feb 12 '21 at 22:44
  • I know what it returns. I tried it. Try it without the ^. It returns the same exact thing. What does "and exclude ," mean exactly? – Scratte Feb 12 '21 at 23:12
  • Thanks. This was my mistake. I correct this. – Md Kawser Habib Feb 13 '21 at 03:06
0

Try this:

List<Integer> results = new ArrayList();

Pattern p = Pattern.compile("(?:(?:A=)([^,]*))");
Matcher m = null;
     
for (String tmp : OriginalList) {
    m = p.matcher(tmp);
    if (m.find()) {
     int r = Integer.parseInt(m.group(0).replace("A=", ""));
        results.add(r);
    }
        
}
Rolud
  • 121
  • 1
  • 8
  • I imagine `m` is a `Matcher`. `p` is a `Pattern`. What regex did you use for `compile()`? Can you add the rest of you code? It would also help if you could explain it. "Try this" isn't very helpful. – Scratte Feb 12 '21 at 19:33
  • Oh, yes! Maybe copying code or merging something broke up. Fixed! – Rolud Feb 15 '21 at 16:34
  • Notice that this post was merged into another Question. Perhaps you'd like to update it or add the key/values that is relevant to your particular regex. It would also be helpful if you explained the regex you're using. Why are you taking `group(0)` and then removing `A=` when you are capturing the value with `group(1)`? – Scratte Feb 15 '21 at 16:48
0

This may be implemented using Stream API by simple splitting of each string in the input list by comma and Stream::flatMap

// assuming A is not always at the beginning
List<String> list = Arrays.asList(
        "A=1,B=2,C=3",
        "A=11,B=12,C=13,D=15",
        "A=5,B=4,C=9,D=10,E=13",
        "B=20,C=91,D=40,E=33",
        "B=27, A=19, C=37, A=77");

List<Integer> aNums = list.stream() // Stream<String>
    .flatMap(
        s -> Arrays.stream(s.split("\\s*,\\s*")) // Stream<String> pairs of letter=digits
                   .filter(pair -> pair.startsWith("A="))
                   .map(pair -> Integer.valueOf(pair.substring(2)))
    )
    .collect(Collectors.toList());
System.out.println(aNums);

Output:

[1, 11, 5, 19, 77]

Update
A pattern to split an input string and keep only the digits related to A may be applied as follows:

Pattern splitByA = Pattern.compile("A\\s*=\\s*|\\s*,\\s*|[^A]\\s*=\\s*\\d+");
List<Integer> aNums2 = list.stream()
    .flatMap(splitByA::splitAsStream) // Stream<String>
    .filter(Predicate.not(String::isEmpty)) // need to remove empty strings
    .map(Integer::valueOf)
    .collect(Collectors.toList());
System.out.println(aNums2);

Output is the same

[1, 11, 5, 19, 77]
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42