I want to get either empty list or list of strings from CSV. And I tried below approach:
String valueStr = "US,UK";
List<String> countryCodes = StringUtils.isBlank(valueStr)
? Collections.emptyList()
: Arrays.stream(valueStr.split(DELIMITER))
.map(String::trim)
.collect(Collectors.toList());
How can I make it more concise without ternary operator, keeping it easy as well? This works fine. Just checking other approaches.
static Pattern p = Pattern.compile(DELIMITER);
public static List<String> getIt(String valueStr) {
return Optional.ofNullable(valueStr)
.map(p::splitAsStream)
.map(x -> x.map(String::trim).collect(Collectors.toList()))
.orElse(Collections.emptyList());
}
You can filter:
List<String> countryCodes = Arrays.stream(
StringUtils.trimToEmpty(valueStr).split(DELIMITER))
.filter(v -> !v.trim().isEmpty())
.collect(Collectors.toList());
The above returns an empty list when tested with a blank. It also excludes blank values (such as the last value from "UK,")
Related
I'm trying to filter a String list like:
List<String> names = ["NAM", "XYZ", "+QWE (HJB)", "+XYZ (NAM)", "(NAM)"];
While using regex I want to compare each String with a string that contains "NAM" or "HJB" and print every string of names containing the filter string out. So in the end it would print out everything with "NAM" in it (also "+XYZ (NAM)", but without the special chars)
My code looks like this but either way I catch everything ("+QWE (HJB)")
regexp3 = RegExp(r'[a-zA-Z]+');
or Nothing
final regexp2 = RegExp(r'^\+.([a-zA-Z]+) \(([a-zA-Z]+).\).$');
because if I only filter with "NAM" (for example) it gives me an null error.
Complete code.
void main() async {
List<String> names= ["TEX","TOL","+TEX (TOL)","+TOL (TEX)", "(NAM)"];
List<String> filter = ["TEX", "TOL"];
final regexp3 = RegExp(r'[a-zA-Z]+');
for(var e in names){
if(filter.contains(regexp3.firstMatch(e)!.group(0))) {
print(e);
}
}
}
Writing regex patterns is indeed giving us a slight nystagmus. However, it is important to be careful about the regex groups numeration. As far as I understand, you want to get the content of group(1) which is captured by the first parenthesis after \+.
To match the strings correctly I also removed a few . characters from the regex pattern.
Replaced the bang operator (!) with ? and ?? in addition to provide safe default value instead of throwing errors on nulls.
Good luck!
void main() async {
List<String> names= ["TEX","TOL","+TEX (TOL)","+TOL (TEX)", "(NAM)"];
List<String> filter = ["TEX", "TOL"];
final regexp3 = RegExp(r'^\+([a-zA-Z]+) \(([a-zA-Z]+)\)$');
for(var e in names){
var regroup = regexp3.firstMatch(e)?.group(1);
if(filter.contains(regroup)) {
print(e + '\t\t' + (regroup ?? ''));
}
}
}
i want to get an element from a list of string and get the position of a char in this list by using linq ?
Example :
List<string> lines = new List<string> { "TOTO=1", "TATA=2", "TUTU=3"}
I want to extract the value 1 from TOTO in the list
here is the begin of my code
var value= lines.ToList().Single(x =>x.Contains("TOTO=")).ToString().Trim();
How to continue this code to extract 1 ?
Add this :
value = value[(value.LastIndexOf('=') + 1)..];
Using LINQ you can do this:
List<string> lines = new List<string> { "TOTO=1", "TATA=2", "TUTU=3" };
int value = lines
.Select(line => line.Split('='))
.Where(parts => parts[0] == "TOTO")
.Select(parts => int.Parse(parts[1]))
.Single();
If you always expect each item in that list to be in the proper format then this should work, otherwise you'd need to add some validation.
Similar to What #jtate proposed, Some minor enhancements can help.
int value = lines
.Select(line => line.Split(new []{ '=' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => string.Equals(parts[0], "TOTO", StringComparison.InvariantCultureIgnoreCase))
.Select(parts => int.Parse(parts[1]))
.SingleOrDefault();
SingleOrDefault - If you don't find any elements matching your constraints, Single() would thow an exception. Here, SingleOrDefault would return 0;
String.Equals - would take care of any upper lowere or any culture related problems.
StringSplitOptions.RemoveEmptyEntries - would limit some unecessary iterations and improve performance.
Also see if you need int.TryParse instead of int.Prase. All these checks would help cover edges cases in production
I have an array list
List<String> items = Arrays.asList("date=2017-01-10", "date=2017-02-10");
I want to convert this list and have quotes for each item in the list so
date=2017-01-10 can be converted to date='2017-01-10'
List<String> items = Arrays.asList("date='2017-01-10'", "date='2017-02-10'");
How can i do in Java 8 ?
UPDATE: I want to use regex to achieve this.
You can use replaceAll with this regex ^(.*?)=(.*?)$ which return two groups one before the = the second after, so you can replace the input with group 1 $1 followed by =' then group 2 between two quotes '$2':
List<String> items = Arrays.asList("date=2017-01-10", "date=2017-02-10");
items = items.stream()
.map(t -> t.replaceAll("(.*?)=(.*?)$", "$1='$2'"))
.collect(Collectors.toList());
items.forEach(System.out::println);
Outputs
date='2017-01-10'
date='2017-02-10'
I have seen your profile, I found you start to Java. so I'll give you the answer just for learning purpose.
use lookbehind to skip replacement. e.g: (?<==)
use group to capture the matched result in group, e.g: (.+)
use $ sign to references to captured group, e.g: $1.
List<String> result = items.stream()
// match `=` ---v
.map(it -> it.replaceFirst("(?<==)(.+)", "'$1'"))
// capture the rest substring in group ---^
.collect(Collectors.toList());
You also can simply to use String#substring to achieve your way. for example:
int prefix = "date=".length();
List<String> result =items.stream()
.map(it->String.format("date='%s'",it.substring(prefix)))
.collect(Collectors.toList());
you can use the following code
List<String> items = Arrays.asList("date=2017-01-10", "date=2017-02-10"), result = new ArrayList<String>();
for(String item : items){
item = item.replace("date=", "date='");
item = item + "'";
result.add(item);
}
for(String item : result){
System.out.println(item);
}
demo
I have a string like below:
SOMETEXT(ABC, DEF, 5, 78.0, MNO)
I want to parse it using regex to get the List<String> of ABC, DEF and MNO. ie. I want to avoid numbers of any type and extract only text.
At large, I have a structure like below:
class Detail {
String name;
String type;
}
// Sample values of name = "test1" type = "SOMETEXT(ABC,5)"
// Sample values of name = "test2" type = "SOMETEXT(ABC,DEF,2.2)"
// Sample values of name = "test3" type = "SOMETEXT(ABC,DEF)"
From the List<Detail> I want to get Map<String, List<String>> where list<String> is extracted texts from type and key is name, in java 8 way using streams if possible.
Till now I had to get only first text from the string and i did that as below:
Map<String, List<String>> assignOperatorMap = details
.stream()
.collect(groupingBy(md -> md.getName(), mapping((Details m) ->
m.getType().substring(m.getType().indexOf("(") + 1,
m.getType().indexOf(")")).split("\\,")[0] ,
Collectors.toList()
)));
Above code gives me:
{test1=[ABC], test2=[ABC], test3=[ABC]} that is only first value.
How about this:
List<Detail> details = new ArrayList<>();
details.add(new Detail("test1", "SOMETEXT(ABC,5)"));
details.add(new Detail("test2", "SOMETEXT(ABC,DEF,2.2)"));
details.add(new Detail("test3", "SOMETEXT(ABC,DEF)"));
Map<String, List<String>> assignOperatorMap = details.stream()
.flatMap(d -> Arrays.stream(d.getType()
.replaceAll("\\w+\\((.*)\\)", "$1")
.split(","))
.filter(s -> s.matches("[A-Za-z_]+"))
.map(s -> new SimpleEntry<>(d.getName(), s)))
.collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList())));
System.out.println(assignOperatorMap); // {test2=[ABC, DEF], test3=[ABC, DEF], test1=[ABC]}
The idea is to first capture the string in between the parenthesis with: .replaceAll("\\w+\\((.*)\\)", "$1"), then split it by , and filter out what doesn't match [A-Za-z_]+.
There is also a trick of creating a bunch of Entry<String, String> (Name, Type), to avoid having to stream twice, since every Detail can now yield multiple type strings, we have to somehow flatten them into a List<String> (instead of a List<String[]>). (preferably that would be done with Java 9's flatMapping collector, but it's not here yet).
how can i extend this regex to ignore some texts for example HOURS, MINUTES
You can create a Set<String> with the words you want to ignore, and filter based on that in a second filter call:
Set<String> ignore = new HashSet<>();
ignore.add("HOURS");
ignore.add("MINUTES");
...
.filter(s -> s.matches("[A-Za-z_]+"))
.filter(s -> !ignore.contains(s)) // <-- extra filter call
.map(s -> new SimpleEntry<>(d.getName(), s)))
...
You could try something like this if order doesn't matter:
final List<Detail> details = Arrays.asList(
new Detail("test1", "SOMETEXT(ABC, DFD)"),
new Detail("test2", "SOMETEXT(ABC,DEF,2.2)"),
new Detail("test3", "SOMETEXT(ABC,DEF,GHF)")
);
final Map<String, List<String>> map = details
.stream()
.collect(Collectors.groupingBy(
Detail::getName,
Collectors.mapping(
detail -> {
final String[] values = detail.getType().split("[,(). 0-9]+");
return Arrays.copyOfRange(values, 1, values.length);
},
Collector.of(ArrayList::new,
(list, array) -> list.addAll(Arrays.asList(array)),
(source, target) -> {
source.addAll(target);
return source;
}
)
)
));
System.out.println(map);
// Output: {test2=[ABC, DEF], test3=[ABC, DEF, GHF], test1=[ABC, DFD]}
Im trying to create a list of strings using some recursion.
Basically i want to take a part of a string up to a certain point. Create a list from that and then process the rest of the string through recursion.
type DocName = FilePath
type Line = (Int,String)
type Document = [Line]
splitLines :: String -> Document
splitLines [] = []
splitLines str | length str == 0 = []
| otherwise = zip [0..(length listStr)] listStr
where
listStr = [getLine] ++ splitLines getRest
getLine = (takeWhile (/='\n') str)
getRest = (dropWhile (=='\n') (dropWhile (/='\n') str))
Thats what i got. But it just concats the strings back together since they are list of characters themselves. But i want to create a list of strings.
["test","123"] if the input was "test\n123\n"
Thanks
If you try to compile your code, you'll get an error message telling you that in the line
listStr = [getLine] ++ splitLines getRest
splitLines getRest has type Document, but it should have type [String]. This is easy enough to understand, since [getLine] is a list of strings (well a list of one string) and so it can only be concatenated with another list of strings, not a list of int-string-tuples.
So to fix this we can use map to replace each int-string-tuple in the Document with only the string to get a list of strings, i.e.:
listStr = [getLine] ++ map snd (splitLines getRest)
After changing the line to the above your code will compile and run just fine.
But it just concats the strings back together since they are list of characters themselves.
I'm not sure why you think that.
The reason your code did not compile was because of the type of splitLines as I explained above. Once you fix that error, the code behaves exactly as you want it to, returning a list of integer-string-tuples. At no point are strings concatenated.
Well, if you wrote this just to practice recursion then it is fine once you fix error mentioned by sepp2k. But in real code, I would prefer -
splitLines str = zip [0..] (lines str)
Or even
splitLines = zip [0..] . lines