getting a list of string placeholders using java stream - java

I have a string with a number of place holders, is there any way if we can collect all the place holders in one go with the help of java streams?
Input:
<Html>
<Table>
<TR><TD>||BuySell||</TD></TR>
<TR><TD>||ExchangeName||</TD></TR>
</Table>
</Html>
Output:
List<String> placeholders = [BuySell,ExchangeName]

This can be done using a helper function.
BiFunction<Matcher, Function<Matcher, Object>, Collection<?>> placeHolderExtractor = (mch, extracter) -> {
List<Object> list = new ArrayList<>();
while(mch.find()) {
list.add(extracter.apply(mch));
}
return list;
};
String htmlStr = "<Html> <Table> <TR><TD>||BuySell||</TD></TR> <TR><TD>||ExchangeName||</TD></TR> </Table></Html>";
String regex = "(\\|\\|)([\\w]+)\\1";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(htmlStr);
List<String> placeHolderList = placeHolderExtractor.apply(matcher, macher -> macher.group(2))
.stream()
.map(String::valueOf)
.collect(Collectors.toList());

Related

How to get an HashMap<String,ArrayList<String>> from a given String Java

So I receive a String that contains a HashMap<String, ArrayList<String>> inside how do I debunk it till I get myself a new HashMap from that String.
This is the string :
String s= "{Lobby1=[John, Ana], Lobby2=[Tomas, Peter]}"
Keep in mind the string can be longer depending on the number of entries or puts
This is what i did to get ride of the "{ }":
s=s.substring(1,s.length()-1);
This gets me :
Lobby1=[John, Ana], Lobby2=[Tomas, Peter]
I don't know what to do now, how do I get an Arraylist and a String from that.
How about something like that:
private static final Pattern RE = Pattern.compile("^\\s*([^=\\[\\]]+)\\s*=\\s*\\[([^\\]]*)\\]\\s*(?:,(.*))?$");
public static void main(String args[]) {
String s = "Lobby1=[John, Ana], Lobby2=[Tomas, Peter]";
Map<String,List<String>> map = new HashMap<>();
Matcher matcher = RE.matcher(s);
while (matcher.matches()) {
String name = matcher.group(1);
List<String> list = Arrays.asList(
matcher.group(2).split("\\s*,\\s*"));
map.put(name, list);
String tail = matcher.group(3);
if (tail == null) {
break;
}
matcher = RE.matcher(tail);
}
System.out.println(map);
}

Get,Put key and values from nested hashmap

I want to create a nested HashMap which returns the frequency of terms among multiple files. Like,
Map<String, Map<String, Integer>> wordToDocumentMap=new HashMap<>();
I have been able to return the number of times a term appears in a file.
Map<String, Integer> map = new HashMap<>();//for frequecy count
String str = "Wikipedia is a free online encyclopedia, created and edited by
volunteers around the world."; //String str suppose a file a.java
// The query string
String query = "edited Wikipedia volunteers";
// Split the given string and the query string on space
String[] strArr = str.split("\\s+");
String[] queryArr = query.split("\\s+");
// Map to hold the frequency of each word of query in the string
Map<String, Integer> map = new HashMap<>();
for (String q : queryArr) {
for (String s : strArr) {
if (q.equals(s)) {
map.put(q, map.getOrDefault(q, 0) + 1);
}
}
}
// Display the map
System.out.println(map);
In my code its count the frequency of the given query Individually. But I want to Map the query term and its frequency with its filenames. I have searched around the web for a solution but am finding it tough to find a solution that applies to me. Any help would be appreciated!
I hope I'm understanding you correctly.
What you want is to be able to read in a list of files and map the file name to the map you create in the code above. So let's start with your code and let's turn it into a function:
public Map<String, Integer> createFreqMap(String str, String query) {
Map<String, Integer> map = new HashMap<>();//for frequecy count
// The query string
String query = "edited Wikipedia volunteers";
// Split the given string and the query string on space
String[] strArr = str.split("\\s+");
String[] queryArr = query.split("\\s+");
// Map to hold the frequency of each word of query in the string
Map<String, Integer> map = new HashMap<>();
for (String q : queryArr) {
for (String s : strArr) {
if (q.equals(s)) {
map.put(q, map.getOrDefault(q, 0) + 1);
}
}
}
// Display the map
System.out.println(map);
return map;
}
OK so now you have a nifty function that makes a map from a string and a query
Now you're going to want to set up a system for reading in a file to a string.
There are a bunch of ways to do this. You can look here for some ways that work for different java versions: https://stackoverflow.com/a/326440/9789673
lets go with this (assuming >java 11):
String content = Files.readString(path, StandardCharsets.US_ASCII);
Where path is the path to the file you want.
Now we can put it all together:
String[] paths = ["this.txt", "that.txt"]
Map<String, Map<String, Integer>> output = new HashMap<>();
String query = "edited Wikipedia volunteers"; //String query = "hello";
for (int i = 0; i < paths.length; i++) {
String content = Files.readString(paths[i], StandardCharsets.US_ASCII);
output.put(paths[i], createFreqMap(content, query);
}

Process the result multiple times using Java Stream

How do I convert to Java Stream the for-loop part in the following codes?
Map<String, String> attributes = new HashMap() {{
put("header", "Hello, world!");
put("font", "Courier New");
put("fontSize", "14px");
put("color", "#0000ff");
put("content", "hello, world" +
"");
}};
Path templatePath = Paths.get("C:/workspace/spring-boot-demos/something.tpl");
List<String> lines = Files.readAllLines(templatePath).stream()
.filter(Objects::nonNull)
.map(line -> {
String parsedLine = "";
for (int i = 0; i < attributes.size(); i++) {
if (parsedLine.equals("")) parsedLine = this.parse(attributes, line);
else parsedLine = this.parse(attributes, parsedLine);
}
return parsedLine;
})
.collect(Collectors.toList());
public String parse(Map<String, String> attributes, String line) {
return attributes.entrySet().stream()
.filter(attributeMap -> line.indexOf("{{" + attributeMap.getKey() + "}}") != -1)
.map(attributeMap -> {
String attributeKey = attributeMap.getKey();
String attributeValue = attributeMap.getValue();
String newLine = line.replace("{{" + attributeKey + "}}", attributeValue);
return newLine;
})
.findFirst().orElse(line);
}
I find it difficult to implement it in Java 8 Stream way so that I'm forced to do it old for-loop construct. The reason behind this is that for every line of string, there might one or more placeholder (using {{}} delimiter). I have the list (attributes, HashMap) of the value to replace them. I want to make sure that before the loop of Stream leave every line, all of the placeholders must replaced with the value from the Map. Without doing this, only the first placeholder is being replaced.
Thank you.
Update:
This is the content of something.tpl file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>HTML Generator</title>
<style>
body {
font-family: "{{font}}";
font-size: {{fontSize}};
color: {{color}};
}
</style>
</head>
<body>
<header>{{header}}</header> {{content}}
<main>{{content}}</main>
<h1>{{header}}</h1>
</body>
</html>
So you want to replace every token in the given List ?
Here's how I would do it with streams :
Iterate over lines
parse each line only once
the parsing iterates over attributes, and replaces the current attribute
The String[] pLine may be was you were missing...
package so20190412;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Snippet {
public Snippet() {
}
public static void main(String[] args) {
Map<String, String> attributes = new HashMap<String, String> () {{
put("john", "doe");
put("bruce", "wayne");
}};
List<String> lines = Arrays.asList("My name is {{john}}, not {{bruce}}.", "Hello, {{john}}!", "How are you doing?");
new Snippet().replaceAll(attributes, lines).forEach(System.out::println);
}
public List<String> replaceAll(Map<String, String> attributes, List<String> lines) {
return lines.stream().map(l -> parse(attributes, l)).collect(Collectors.toList());
}
public String parse(Map<String, String> attributes, String line) {
String[] pLine = {line};
attributes.entrySet().stream()
.forEach(attr -> {
String attributeKey = attr.getKey();
String attributeValue = attr.getValue();
String newLine = pLine[0].replace("{{" + attributeKey + "}}", attributeValue);
pLine[0] = newLine;
});
return pLine[0];
}
}
Don't hesitate to ask if you don't understand some part.
HTH!

unique number of words in a propertyfile

What is the optimum way to count the unique number of words in a propertyfile (Just the Values) in java (java 1.8)
for example entries may be:
key1=This is my value for error {0}
key2=This is success message.Great.
Output should be 10 (including {0})
What I tried
property.load(in);
Enumeration em = property.keys();
while (em.hasMoreElements()) {
String str = (String) em.nextElement();
completeString =completeString+property.get(str);
}
Set<String> myset=new HashSet<>();
String s[]=completeString.split("[ .]");
for(int i=1;i<s.length;i++){
myset.add(s[i]);
}
for (String sss: myset){
System.out.println(sss);
}
System.out.println(myset.size());
Do we have a simpler way in java 1.8
Data used :
I used a dummy Properties
Properties prop = new Properties();
prop.put("A", "This is my value for error {0}");
prop.put("B", "This is success message.Great.");
Good old Java:
Using the same logic you used, you can simply split the String of each property in the iteration :
Set<String> set = new HashSet<>();
Enumeration em = property.keys();
while (em.hasMoreElements()) {
String str = (String) em.nextElement();
for(String s : str.split("[ .]")){
set.add(s);
}
}
In Java 8 - Stream API :
Define the pattern to split each "word".
Pattern pattern = Pattern.compile("[ .]");
Now, first let's get our Stream<String> for our values.
You can either take a List<Object> :
Stream<String> stream =
//Create a `List<Object>` from the enumeration and stream it
Collections.list(prop.elements()).stream()
//Convert in String
.map(o -> (String)o);
Or Stream the Map.Entry of the Properties :
Stream<String> stream =
prop.entrySet().stream() //Iterate the Map.Entry<Object,Object>
.map(e -> (String)e.getValue())
(Not sure which is more efficient)
Then, all you have to do is to flatMap the Stream to split each String into new Stream<String>.
stream.flatMap(pattern::splitAsStream) //split based on the pattern define and return a new `Stream<String>`
Then collect the Stream into a Set
.collect(Collectors.toSet()); //collect in a `Set<String>`
The result would be a nice Set printed like:
[Great, success, for, This, {0}, is, my, error, message, value]
Summary :
Set<String> set =
prop.entrySet().stream()
.map(e -> (String)e.getValue())
.flatMap(Pattern.compile(pattern)::splitAsStream)
.collect(Collectors.toSet());

Java: How to fill placeholders in a text with Map<String,String>?

I am working with a code where I want fill several string place holders with another strings. This is the example text I have used to test my code.
String myStr = "Media file %s of size %s has been approved"
This is how I fill the place holders. Since I expect to use several place holders I have used java Map<>.
Map<String, String> propMap = new HashMap<String,String>();
propMap.put("file name","20mb");
String newNotification = createNotification(propMap);
I used following method to create the string.
public String createNotification(Map<String, String> properties){
String message = "";
message = String.format(myStr, properties);
return message;
}
How do I replace two of '%s' with "file name" and "20mb"?
That's not what a Map is intended to do.
What you add is an entry "file name" -> "20 mb", which basically means the property "file name" has the value "20 mb". What you are trying to do with it is "maintain a tuple of items".
Note that the formatting string has a fixed amount of placeholder; you want a data structure that contains exactly the same amount of items; so essentially an array or a List.
Thus, what you want to have is
public String createNotification(String[] properties) {
assert(properties.length == 2); // you might want to really check this, you will run into problems if it's false
return String.format("file %s has size %s", properties);
}
If you want to create notifications of all items in a map, you need to do something like this:
Map<String,String> yourMap = //...
for (Entry<String,String> e : yourMap) {
System.out.println(createNotification(e.getKey(), e.getValue()));
}
Your approach to String#format is wrong.
It expects a variable amount of objects to replace the placeholders as the second argument, not a map. To group them all together, you can use an array or a list.
String format = "Media file %s of size %s has been approved";
Object[] args = {"file name", "20mb"};
String newNotification = String.format(format, args);
You can simply do this formatting using var-args:
String myStr = "Media file %s of size %s has been approved";
String newNotification = createNotification(myStr, "file name", "20mb");
System.out.println(newNotification);
Pass var-args in createNotification method, here is the code:
public static String createNotification(String myStr, String... strings){
String message = "";
message=String.format(myStr, strings[0], strings[1]);
return message;
}
I think %s is Python’s grammar to place holder, can’t use this in Java environment; and your method createNotification() defined needs two parameters, can’t only give one.
After trying several ways finally found a good solution. Place holders must be like this [placeholder] .
public String createNotification(){
Pattern pattern = Pattern.compile("\\[(.+?)\\]");
Matcher matcher = pattern.matcher(textTemplate);
HashMap<String,String> replacementValues = new HashMap<String,String>();
StringBuilder builder = new StringBuilder();
int i = 0;
while (matcher.find()) {
String replacement = replacementValues.get(matcher.group(1));
builder.append(textTemplate.substring(i, matcher.start()));
if (replacement == null){ builder.append(matcher.group(0)); }
else { builder.append(replacement); }
i = matcher.end();
}
builder.append(textTemplate.substring(i, textTemplate.length()));
return builder.toString()
}

Categories