docx4j find and replace - java

I have docx document with some placeholders. Now I should replace them with other content and save new docx document. I started with docx4j and found this method:
public static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
List<Object> result = new ArrayList<Object>();
if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();
if (obj.getClass().equals(toSearch))
result.add(obj);
else if (obj instanceof ContentAccessor) {
List<?> children = ((ContentAccessor) obj).getContent();
for (Object child : children) {
result.addAll(getAllElementFromObject(child, toSearch));
}
}
return result;
}
public static void findAndReplace(WordprocessingMLPackage doc, String toFind, String replacer){
List<Object> paragraphs = getAllElementFromObject(doc.getMainDocumentPart(), P.class);
for(Object par : paragraphs){
P p = (P) par;
List<Object> texts = getAllElementFromObject(p, Text.class);
for(Object text : texts){
Text t = (Text)text;
if(t.getValue().contains(toFind)){
t.setValue(t.getValue().replace(toFind, replacer));
}
}
}
}
But that only work rarely because usually the placeholders splits across multiple texts runs.
I tried UnmarshallFromTemplate but it work rarely too.
How this problem could be solved?

You can use VariableReplace to achieve this which may not have existed at the time of the other answers.
This does not do a find/replace per se but works on placeholders eg ${myField}
java.util.HashMap mappings = new java.util.HashMap();
VariablePrepare.prepare(wordMLPackage);//see notes
mappings.put("myField", "foo");
wordMLPackage.getMainDocumentPart().variableReplace(mappings);
Note that you do not pass ${myField} as the field name; rather pass the unescaped field name myField - This is rather inflexible in that as it currently stands your placeholders must be of the format ${xyz} whereas if you could pass in anything then you could use it for any find/replace. The ability to use this also exists for C# people in docx4j.NET
See here for more info on VariableReplace or here for VariablePrepare

Good day, I made an example how to quickly replace text to something you need
by regexp. I find ${param.sumname} and replace it in document.
Note, you have to insert text as 'text only'!
Have fun!
WordprocessingMLPackage mlp = WordprocessingMLPackage.load(new File("filepath"));
replaceText(mlp.getMainDocumentPart());
static void replaceText(ContentAccessor c)
throws Exception
{
for (Object p: c.getContent())
{
if (p instanceof ContentAccessor)
replaceText((ContentAccessor) p);
else if (p instanceof JAXBElement)
{
Object v = ((JAXBElement) p).getValue();
if (v instanceof ContentAccessor)
replaceText((ContentAccessor) v);
else if (v instanceof org.docx4j.wml.Text)
{
org.docx4j.wml.Text t = (org.docx4j.wml.Text) v;
String text = t.getValue();
if (text != null)
{
t.setSpace("preserve"); // needed?
t.setValue(replaceParams(text));
}
}
}
}
}
static Pattern paramPatern = Pattern.compile("(?i)(\\$\\{([\\w\\.]+)\\})");
static String replaceParams(String text)
{
Matcher m = paramPatern.matcher(text);
if (!m.find())
return text;
StringBuffer sb = new StringBuffer();
String param, replacement;
do
{
param = m.group(2);
if (param != null)
{
replacement = getParamValue(param);
m.appendReplacement(sb, replacement);
}
else
m.appendReplacement(sb, "");
}
while (m.find());
m.appendTail(sb);
return sb.toString();
}
static String getParamValue(String name)
{
// replace from map or something else
return name;
}

I created a library to publish my solution because it's quite a lot of code: https://github.com/phip1611/docx4j-search-and-replace-util
The workflow is the following:
First step:
// (this method was part of your question)
List<Text> texts = getAllElementFromObject(docxDocument.getMainDocumentPart(), Text.class);
This way we get all actual Text-content in the correct order but without style markup in-between. We can edit the Text-objects (by setValue) and keep styles.
Resulting problem: Search-text/placeholders can be split accoss multiple Text-instances (because there can be style markup that is invisble in-between in original document), e.g. ${FOOBAR}, ${ + FOOBAR}, or $ + {FOOB + AR}
Second step:
Concat all Text-objects to a full string / "complete string"
Optional<String> completeStringOpt = texts.stream().map(Text::getValue).reduce(String::concat);
Third step:
Create a class TextMetaItem. Each TextMetaItem knows for it's Text-object where it's content begins and ends in the complete string. E.g. If the Text-objects for "foo" and "bar" results in the complete string "foobar" than indices 0-2 belongs to "foo"-Text-object and 3-5 to "bar"-Text-object. Build a List<TextMetaItem>
static List<TextMetaItem> buildMetaItemList(List<Text> texts) {
final int[] index = {0};
final int[] iteration = {0};
List<TextMetaItem> list = new ArrayList<>();
texts.forEach(text -> {
int length = text.getValue().length();
list.add(new TextMetaItem(index[0], index[0] + length - 1, text, iteration[0]));
index[0] += length;
iteration[0]++;
});
return list;
}
Fourth step:
Build a Map<Integer, TextMetaItem> where the key is the index/char in the complete string. This means the map's length equals completeString.length()
static Map<Integer, TextMetaItem> buildStringIndicesToTextMetaItemMap(List<Text> texts) {
List<TextMetaItem> metaItemList = buildMetaItemList(texts);
Map<Integer, TextMetaItem> map = new TreeMap<>();
int currentStringIndicesToTextIndex = 0;
// + 1 important here!
int max = metaItemList.get(metaItemList.size() - 1).getEnd() + 1;
for (int i = 0; i < max; i++) {
TextMetaItem currentTextMetaItem = metaItemList.get(currentStringIndicesToTextIndex);
map.put(i, currentTextMetaItem);
if (i >= currentTextMetaItem.getEnd()) {
currentStringIndicesToTextIndex++;
}
}
return map;
}
interim result:
Now you have enough metadata to delegate every action you want to do on the complete string to the corresponding Text object! (To change the content of Text-objects you just need to call (#setValue()) That's all what's needed in Docx4J to edit text. All style info etc will be preserved!
last step: search and replace
build a method that finds all occurrences of your possible placeholders. You should create a class like FoundResult(int start, int end) that stores begin and end indices of a found value (placeholder) in the complete string
public static List<FoundResult> findAllOccurrencesInString(String data, String search) {
List<FoundResult> list = new ArrayList<>();
String remaining = data;
int totalIndex = 0;
while (true) {
int index = remaining.indexOf(search);
if (index == -1) {
break;
}
int throwAwayCharCount = index + search.length();
remaining = remaining.substring(throwAwayCharCount);
list.add(new FoundResult(totalIndex + index, search));
totalIndex += throwAwayCharCount;
}
return list;
}
using this I build a new list of ReplaceCommands. A ReplaceCommand is a simple class and stores a FoundResult and the new value.
next you must order this list from the last item to the first (order by position in complete string)
now you can write a replace all algorithm because you know what action needs to be done on which Text-object. We did (2) so that replace operations won't invalidate indices of other FoundResults.
3.1.) find Text-object(s) that needs to be changed
3.2.) call getValue() on them
3.3.) edit the string to the new value
3.4.) call setValue() on the Text-objects
This is the code that does all the magic. It executes a single ReplaceCommand.
/**
* #param texts All Text-objects
* #param replaceCommand Command
* #param map Lookup-Map from index in complete string to TextMetaItem
*/
public static void executeReplaceCommand(List<Text> texts, ReplaceCommand replaceCommand, Map<Integer, TextMetaItem> map) {
TextMetaItem tmi1 = map.get(replaceCommand.getFoundResult().getStart());
TextMetaItem tmi2 = map.get(replaceCommand.getFoundResult().getEnd());
if (tmi2.getPosition() - tmi1.getPosition() > 0) {
// it can happen that text objects are in-between
// we can remove them (set to null)
int upperBorder = tmi2.getPosition();
int lowerBorder = tmi1.getPosition() + 1;
for (int i = lowerBorder; i < upperBorder; i++) {
texts.get(i).setValue(null);
}
}
if (tmi1.getPosition() == tmi2.getPosition()) {
// do replacement inside a single Text-object
String t1 = tmi1.getText().getValue();
int beginIndex = tmi1.getPositionInsideTextObject(replaceCommand.getFoundResult().getStart());
int endIndex = tmi2.getPositionInsideTextObject(replaceCommand.getFoundResult().getEnd());
String keepBefore = t1.substring(0, beginIndex);
String keepAfter = t1.substring(endIndex + 1);
tmi1.getText().setValue(keepBefore + replaceCommand.getNewValue() + keepAfter);
} else {
// do replacement across two Text-objects
// check where to start and replace
// the Text-objects value inside both Text-objects
String t1 = tmi1.getText().getValue();
String t2 = tmi2.getText().getValue();
int beginIndex = tmi1.getPositionInsideTextObject(replaceCommand.getFoundResult().getStart());
int endIndex = tmi2.getPositionInsideTextObject(replaceCommand.getFoundResult().getEnd());
t1 = t1.substring(0, beginIndex);
t1 = t1.concat(replaceCommand.getNewValue());
t2 = t2.substring(endIndex + 1);
tmi1.getText().setValue(t1);
tmi2.getText().setValue(t2);
}
}

This can be a problem. I cover how to mitigate broken-up text runs in this answer here: https://stackoverflow.com/a/17066582/125750
... but you might want to consider content controls instead. The docx4j source site has various content control samples here:
https://github.com/plutext/docx4j/tree/master/src/samples/docx4j/org/docx4j/samples

Related

How can two CSV files with the same data but different column orders be validated in Java?

I am trying to compare two CSV files that have the same data but columns in different orders. When the column orders match, the following code works: How can I tweak my following code to make it work when column orders don't match between the CSV files?
Set<String> source = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(sourceFile)));
Set<String> target = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(targetFile)));
return source.containsAll(target) && target.containsAll(source)
For example, the above test pass when the source file and target file are in this way:
source file:
a,b,c
1,2,3
4,5,6
target file:
a,b,c
1,2,3
4,5,6
However, the source file is same, but if the target file is in the following way, it doesn't work.
target file:
a,c,b
1,3,2
4,6,5
A Set relies on properly functioning .equalsmethod for comparison, whether detecting duplicates, or comparing it's elements to those in another Collection. When I saw this question, my first thought was to create a new class for Objects to put into your Set Objects, replacing the String Objects. But, at the time, it was easier and faster to produce the code in my previous answer.
Here is another solution, which is closer to my first thought. To start, I created a Pair class, which overrides .hashCode () and .equals (Object other).
package comparecsv1;
import java.util.Objects;
public class Pair <T, U> {
private final T t;
private final U u;
Pair (T aT, U aU) {
this.t = aT;
this.u = aU;
}
#Override
public int hashCode() {
int hash = 3;
hash = 59 * hash + Objects.hashCode(this.t);
hash = 59 * hash + Objects.hashCode(this.u);
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj == null) { return false; }
if (getClass() != obj.getClass()) { return false; }
final Pair<?, ?> other = (Pair<?, ?>) obj;
if (!Objects.equals(this.t, other.t)) {
return false;
}
return Objects.equals(this.u, other.u);
} // end equals
} // end class pair
The .equals (Object obj) and the .hashCode () methods were auto-generated by the IDE. As you know, .hashCode() should always be overridden when .equals is overridden. Also, some Collection Objects, such as HashMap and HashSet rely on proper .hashCode() methods.
After creating class Pair<T,U>, I created class CompareCSV1. The idea here is to use a Set<Set<Pair<String, String>>> where you have Set<String> in your code.
A Pair<String, String> pairs a value from a column with the header for the column in which it appears.
A Set<Pair<String, String>> represents one row.
A Set<Set<Pair<String, String>>> represents all the rows.
package comparecsv1;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public final class CompareCSV1 {
private final Set<Set<Pair<String, String>>> theSet;
private final String [] columnHeader;
private CompareCSV1 (String columnHeadings, String headerSplitRegex) {
columnHeader = columnHeadings.split (headerSplitRegex);
theSet = new HashSet<> ();
}
private Set<Pair<String, String>> createLine
(String columnSource, String columnSplitRegex) {
String [] column = columnSource.split (columnSplitRegex);
Set<Pair<String, String>> lineSet = new HashSet<> ();
int i = 0;
for (String columnValue: column) {
lineSet.add (new Pair (columnValue, columnHeader [i++]));
}
return lineSet;
}
public Set<Set<Pair<String, String>>> getSet () { return theSet; }
public String [] getColumnHeaders () {
return Arrays.copyOf (columnHeader, columnHeader.length);
}
public static CompareCSV1 createFromData (List<String> theData
, String headerSplitRegex, String columnSplitRegex) {
CompareCSV1 result =
new CompareCSV1 (theData.get(0), headerSplitRegex);
for (int i = 1; i < theData.size(); ++i) {
result.theSet.add(result.createLine(theData.get(i), columnSplitRegex));
}
return result;
}
public static void main(String[] args) {
String [] sourceData = {"a,b,c,d,e", "6,7,8,9,10", "1,2,3,4,5"
,"11,12,13,14,15", "16,17,18,19,20"};
String [] targetData = {"c,b,e,d,a", "3,2,5,4,1", "8,7,10,9,6"
,"13,12,15,14,11", "18,17,20,19,16"};
List<String> source = Arrays.asList(sourceData);
List<String> target = Arrays.asList (targetData);
CompareCSV1 sourceCSV = createFromData (source, ",", ",");
CompareCSV1 targetCSV = createFromData (target, ",", ",");
System.out.println ("Source contains target? "
+ sourceCSV.getSet().containsAll (targetCSV.getSet())
+ ". Target contains source? "
+ targetCSV.getSet().containsAll (sourceCSV.getSet())
+ ". Are equal? " + targetCSV.getSet().equals (sourceCSV.getSet()));
} // end main
} // end class CompareCSV1
This code has some things in common with the code in my first answer:
Except for the column header lines, which must be first in the "source" and "Target" data, matching lines in one file can be in a different order in the other file.
I used String [] Objects, with calls to Arrays.asList method as substitutes for your data sources.
It does not contain code to guard against errors, such as lines in the file having different numbers of columns from other lines, or no header line.
I hard coded "," as the String split expression in main. But, the new methods allow the String split expression to be passed. It allows a separate String split expressions for the column header line and the data lines.
Here is some code that could work. It relies on the first line of each file containing column headers.
It's a bit more than a tweak, though. It's an "old dog" approach.
The original code in the question has these lines:
Set<String> source = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(sourceFile)));
Set<String> target = new HashSet<>(org.apache.commons.io.FileUtils.readLines(new File(targetFile)));
With this solution, the data coming in needs more processing before it will be ready to be put into a Set. Those two lines get changed as follows:
List<String> source = (org.apache.commons.io.FileUtils.readLines(new File(sourceFile)));
List<String> target = (org.apache.commons.io.FileUtils.readLines(new File(targetFile)));
This approach will compare column headers in the target file and the source file. It will use that to build an int [] that indicates the difference in column order.
After the order difference array is filled, the data in the file will be put into a pair of Set<List<String>>. Each List<String> will represent one line from the source and target data files. Each String in the List will be data from one column.
In the following code, main is the test driver. Only for my testing purposes, the data files have been replaced by a pair of String [] and reading the file with org.apache.commons.io.FileUtils.readLines has been replaced with Arrays.asList.
package comparecsv;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CompareCSV {
private static int [] columnReorder;
private static void headersOrder
(String sourceHeader, String targetHeader) {
String [] columnHeader = sourceHeader.split (",");
List<String> sourceColumn = Arrays.asList (columnHeader);
columnReorder = new int [columnHeader.length];
String [] targetColumn = targetHeader.split (",");
for (int i = 0; i < targetColumn.length; ++i) {
int j = sourceColumn.indexOf(targetColumn[i]);
columnReorder [i] = j;
}
}
private static Set<List<String>> toSet
(List<String> data, boolean reorder) {
Set<List<String>> dataSet = new HashSet<> ();
for (String s: data) {
String [] byColumn = s.split (",");
if (reorder) {
String [] reordered = new String [byColumn.length];
for (int i = 0; i < byColumn.length; ++i) {
reordered[columnReorder[i]] = byColumn [i];
}
dataSet.add (Arrays.asList (reordered));
} else {
dataSet.add (Arrays.asList(byColumn));
}
}
return dataSet;
}
public static void main(String[] args) {
String [] sourceData = {"a,b,c,d,e", "1,2,3,4,5", "6,7,8,9,10"
,"11,12,13,14,15", "16,17,18,19,20"};
String [] targetData = {"c,b,e,d,a", "3,2,5,4,1", "8,7,10,9,6"
,"13,12,15,14,11", "18,17,20,19,16"};
List<String> source = Arrays.asList(sourceData);
List<String> target = Arrays.asList (targetData);
headersOrder (source.get(0), target.get(0));
Set<List<String>> sourceSet = toSet (source, false);
Set<List<String>> targetSet = toSet (target, true);
System.out.println ( sourceSet.containsAll (targetSet)
+ " " + targetSet.containsAll (sourceSet) + " " +
( sourceSet.containsAll (targetSet)
&& targetSet.containsAll (sourceSet)));
}
}
MethodheadersOrder compares the headers, column by column, and populates the columnReorder array. Method toSet creates the Set<List<String>>, either reordering the columns or not, according to the value of the boolean argument.
For the sake of simplification, this assumes lines are easily split using comma. Data such as dog, "Reginald, III", 3 will cause failure.
In testing this, I found lines in the file can be matched with their counterpart in the other file, regardless of order of the lines. Here is an example:
Source:
a,b,c
1,2,3
4,5,6
7,8,9
Target:
a,b,c
4,5,6
7,8,9
1,2,3
The result would be the contents match.
I believe this would match a result from the O/P question code. However, for this solution to work, the first line in each file must contain column headers.

Given string filter a part of it based on a pattern then add to array-list

Given a string with repeating pattern here:
String s = NameJJ,ABC/firstname,120, NameBB,ABC/secondname,100,NameCC,ABC/thirdname,150,NameJJ,ABC/firstname,200,NameBB,ABC/secondname,300,NameCC,ABC/thirdname,500
Essentially I want way where when NameJJ and firstname are beside each other like at top it stores value beside them which is arraylist1 = [120,200].
Essentially I want way where all field with NameBB and secondname are beside each other to store the value beside them which is arraylist2 = [100,300].
Essentially I want way where all field with NameJJ and thirdname are beside each other to store the value beside them which is arraylist3 = [150,500].
I was trying to do this to say if string contains NameJJ and contains firstname should filter out value beside it but I'm confused on how I would do that, this is the code :
if (s.contains(NameJJ) && s.contains(firstname))
Pattern reg = Pattern.compile(",");
ArrayList<String> SANR = reg.splitAsStream(s)
.filter(role -> role.contains(""))
.map(String::trim)
.collect(Collectors.toCollection(ArrayList::new));
Your String s holds the complete data so if (s.contains(NameJJ) && s.contains(firstname)) will always be true.
You have to split the blocks first, then look at the values.
Your data block contains 3 data each so that's the number you have to divide by in loop condition and multiply with in array access
String s = "NameJJ,ABC/firstname,120, NameBB,ABC/secondname,100,NameCC,ABC/thirdname,150,NameJJ,ABC/firstname,200,NameBB,ABC/secondname,300,NameCC,ABC/thirdname,500";
String[] split = s.split(",");
List<Integer> first = new ArrayList<>();
List<Integer> second = new ArrayList<>();
List<Integer> third = new ArrayList<>();
for (int i = 0; i < split.length / 3; i++) {
// first data block is Name..
// second block is firstname/secondname/thirdname
// third block hold the number you want
if (split[3 * i].trim().equals("NameJJ") && split[3 * i + 1].trim().equals("ABC/firstname")) {
first.add(Integer.parseInt(split[3 * i + 2].trim()));
} else if (split[3 * i].trim().equals("NameBB") && split[3 * i + 1].trim().equals("ABC/secondname")) {
second.add(Integer.parseInt(split[3 * i + 2].trim()));
} else if (split[3 * i].trim().equals("NameCC") && split[3 * i + 1].trim().equals("ABC/thirdname")) {
third.add(Integer.parseInt(split[3 * i + 2].trim()));
}
}
System.out.println(first);
System.out.println(second);
System.out.println(third);
Output is
[120, 200]
[100, 300]
[150, 500]
As I've answered in a previous question of yours, whenever you're having a certain pattern repeating itself, you should use a regex to extract the sub-bits of your pattern.
Streams are not designed to keep track of a "first" or "last" element. They're mainly stateless operations, with a few exceptions for distinct, sorted and similar. Stateless operations start their execution even before the previous one has finished its computation. This is why they have no recollection of a first or last result and also why we cannot use them for this kind of tasks.
What you could do is to employ 3 regex to match your pattern for nameJJ, nameBB and nameCC, and use capturing groups to identity and extract every desired sub-bit.
The following 3 regex can be used to match your patterns:
(\w+)JJ,(\w+)\/(\w+),(\d+)
(\w+)BB,(\w+)\/(\w+),(\d+)
(\w+)CC,(\w+)\/(\w+),(\d+)
Here is a snippet of the code implementation:
public class Main {
public static void main(String[] args) {
String s = "NameJJ,ABC/firstname,120, NameBB,ABC/secondname,100,NameCC,ABC/thirdname,150,NameJJ,ABC/firstname,200,NameBB,ABC/secondname,300,NameCC,ABC/thirdname,500";
List<Integer> listNameJJ = getList("(\\w+)JJ,(\\w+)\\/(\\w+),(\\d+)", s);
List<Integer> listNameBB = getList("(\\w+)BB,(\\w+)\\/(\\w+),(\\d+)", s);
List<Integer> listNameCC = getList("(\\w+)CC,(\\w+)\\/(\\w+),(\\d+)", s);
System.out.println(listNameJJ);
System.out.println(listNameBB);
System.out.println(listNameCC);
}
public static List<Integer> getList(String regex, String str) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
List<Integer> list = new ArrayList<>();
while (matcher.find()) {
list.add(Integer.valueOf(matcher.group(4)));
}
return list;
}
}
Here is also a link to test the code above:
https://ideone.com/TES5UY
Here is another example,
public class Application {
public static void main(String[] args) {
String yourString = "NameJJ,ABC/firstname,120, NameBB,ABC/secondname,100,NameCC,ABC/thirdname,150,NameJJ,ABC/firstname,200,NameBB,ABC/secondname,300,NameCC,ABC/thirdname,500";
String myString = "NameKK,ZXY/kname,510, NameQQ,HLM/kname,210, NameKK,ZXY/kname,210,";
// Test 1
System.out.println(getValues("NameJJ", "firstname", yourString));
// Test 2
System.out.println(getValues("NameKK", "kname", myString));
}
private static List<Integer> getValues(String str1, String str2, String inputString) {
// Note* List is an ordered sequence of elements.
List<Integer> value = new ArrayList<Integer>();
// Extract Values
Matcher m = Pattern.compile("-?\\d+(,\\d+)*?\\.?\\d+?")
.matcher(inputString);
while (m.find()) {
value.add(Integer.parseInt(m.group().trim()));
}
// Extract Keys
List<String> key = List.of(inputString.split("\\d+"));
List<Integer> result = new ArrayList<Integer>();
// Filtering
for (int i = 0; i < value.size(); i++) {
if (key.get(i).trim().contains(str1)
&& key.get(i).trim().contains(str2)) {
result.add(value.get(i));
}
}
return result;
}
}
Console O/P:
[120, 200]
[510, 210]

as I find a string in another java

I wish you can help me I want to do is I have two variables of type string
String text = "HELLO HOW ARE YOU";
String value = "abc";
I want to do is see if any character of the variable values in text and contains tested but I only detects a single character but not
if(text.toUpperCase().contains(value.toUpperCase()))throw new Exception("NOT LETTERS");
You could use the List API...
String text = "HELLO HOW ARE YOU";
String value = "abc";
List<String> left = new ArrayList<>(Arrays.asList(text.toUpperCase().split("")));
List<String> right = new ArrayList<>(Arrays.asList(value.toUpperCase().split("")));
boolean hasAny = left.removeAll(right);
Basically this creates a List of each word then removes all the matches from the second in the first. A return value of true means the first List was modified, meaning it had matching values. You might even be able to compare the difference in size to determine how many matches there were.
You could also use Java 8's Stream API...
String text = "HELLO HOW ARE YOU";
String value = "abc";
List<String> left = new ArrayList<>(Arrays.asList(text.toUpperCase().split("")));
List<String> right = new ArrayList<>(Arrays.asList(value.toUpperCase().split("")));
boolean anyMatch = left.stream().anyMatch((String t) -> {
return right.contains(t);
});
Again, this will simply return true if the first List contains ANY of the values in the second List
Now, if you wanted to know which values actually matched, you might be able to use something like...
Set<String> collect = right.stream().filter((String t) -> {
return left.contains(t);
}).collect(Collectors.toSet());
System.out.println(collect);
Which in you example, would print
[A]
You can try like this:
public static boolean findLetters() {
String text = "HELLO HOW ARE YOU";
String value = "abc";
for (int i = 0; i < value.length(); i++) {
if (text.indexOf(value.toUpperCase().charAt(i)) == -1) {
return false;
}
}
return true;
}
Not sure if I understand the question quite well, but you may try this:
String text = "HELLO HOW ARE YOU";
String value = "abc";
for(char c : value.toUpperCase().toCharArray()) {
if (text.indexOf(c) != -1) {
throw new Exception("NOT LETTERS");
}
}

How to compare two arrays string for each position

I am new to java, and I have this problem; I am working with a webservice from android where I send a request and I get an answer formatted like this string: 1-0,2-0,3-0,4-0,5-0,6-0,7-0,8-0,12-0,13-0 where the number before the "-" means the number of my button and the number after "-" means the button status. I split this string into an array like this:
String buttons = "1-0,2-0,3-0,4-0,5-0,6-0,7-0,8-0,13-0,14-0";
String[] totalButtons = buttons.split(",");
then I make a new request to get the status of my buttons and I get this
String status = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,9-2,10-1,13-4,14-2";
String[] statusButtons = status.split(",");
The number of the buttons are going to be the same all time; in this case 10 buttons.
The problem that I have is how to compare each element of the two arrays if they can change their status every two seconds and I receive more buttons than the first time and I have to change their status with the new value. For example the first element of the array one is equal to the first element of the second array so there is no problem, but the first array does not have two elements in the second array in this case 9-2,10-1 so they should be deleted. The final result should be like this
String buttons = "1-0,2-0,3-0,4-0,5-0,6-0,7-0,8-0,13-0,14-0";
String status = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,9-2,10-1,13-4,14-2";
String finalButtons = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,13-4,14-2";
Here's an idea to get you started;
Map<String,String> buttonStatus = new HashMap<String,String>();
for (String button : totalButtons) {
String parts[] = button.split("-");
buttonStatus.put(parts[0], parts[1]);
}
for (String button : statusButtons) {
String parts[] = button.split("-");
if (buttonStatus.containsKey(parts[0])) {
buttonStatus.put(parts[0], parts[1]);
}
// Java 8 has a "replace" method that will change the value only if the key
// already exists; unfortunately, Android doesn't support it
}
The result will be a map whose keys are taken from the original totalButtons, and whose values will be taken from statusButtons if present. You can go through the keys and values in the Map to get the results, but they won't be in order; if you want them to be in the same order as totalButtons, go through totalButtons again and use buttonStatus.get to get each value.
The javadoc for Map is here.
I would split up each of those again and then compare those values.
ex:
String[] doubleSplit = totalButtons[index].split("-"); // "1-0" -> {"1", "0"}
import java.util.HashMap;
import java.util.Map;
/**
* #author Davide
*/
public class test {
static Map map;
public static void main(String[] args) {
// init value
String buttons = "1-0,2-0,3-0,4-0,5-4,6-0,7-0,8-0,13-0,14-0";
String[] keys = buttons.split("(-[0-9]*,*)");
init(keys);
// new value
String status = "1-0,2-0,3-2,4-0,5-4,6-0,7-4,8-0,9-2,10-1,13-4,14-2";
String[] statusButtons = status.split(",");
update(statusButtons);
print();
}
public static void init(String[] keys) {
map = new HashMap<Integer, Integer>();
for (String k : keys) {
map.put(Integer.valueOf(k), 0);
}
}
public static void update(String[] statusButtons) {
for (String state : statusButtons) {
String[] split = state.split("-");
int k = Integer.valueOf(split[0]);
int v = Integer.valueOf(split[1]);
if (map.containsKey(k)) {
map.put(k, v);
}
}
}
public static void print() {
String out = "";
for (Object k : map.keySet()) {
out += k + "-" + map.get(k) + ",";
}
System.out.println(out.substring(0, out.length() - 1));
}
}

Complex string split in Java

Consider the following String :
5|12345|value1|value2|value3|value4+5|777|value1|value2|value3|value4?5|777|value1|value2|value3|value4+
Here is how I want to split string, split it with + so I get this result :
myArray[0] = "5|12345|value1|value2|value3|value4";
myArray[1] = "5|777|value1|value2|value3|value4?5|777|value1|value2|value3|value4";
if string has doesn't contain char "?" split it with "|" and continue to part II, if string does contain "?" split it and for each part split it with "|" and continue to part II.
Here is part II :
myObject.setAttribute1(newString[0]);
...
myObject.setAttribute4(newString[3]);
Here what I've got so far :
private static String input = "5|12345|value1|value2|value3|value4+5|777|value1|value2|value3|value4?5|777|value1|value2|value3|value4+";
public void mapObject(String input){
String[] myArray = null;
if (input.contains("+")) {
myArray = input.split("+");
} else {
myArray = new String[1];
myArray[0] = input;
}
for (int i = 0; i < myArray.length; i++) {
String[] secondaryArray = null;
String[] myObjectAttribute = null;
if (myArray[i].contains("?")) {
secondaryArray = temporaryString.myArray[i].split("?");
for (String string : secondaryArray) {
myObjectAttribute = string.split("\\|");
}
} else {
myObjectAttribute = myArray[i].toString().split("\\|");
}
myObject.setAttribute1(myObjectAttribute[0]);
...
myObject.setAttribute4(myObjectAttribute[3]);
System.out.println(myObject.toString());
}
Problem :
When I split myArray, going trough for with myArray[0], everything set up nice as it should.
Then comes the myArray[1], its split into two parts then the second part overrides the value of the first(how do I know that?). I've overridden toString() method of myObject, when I finish I print the set values so I know that it overrides it, does anybody know how can I fix this?
I'm not quite sure what the intention is here, but in this snippet of code
secondaryArray = temporaryString.split("?");
for (String string : secondaryArray) {
myObjectAttribute = string.split("\\|");
}
if secondaryArray has two elements after the split operation, you are iterating over each half and re-assigning myObjectAttribute to the output of string.split("\|") each time. It doesn't matter what is in the first element of secondaryArray, as after this code runs myObjectAttribute is going to contain the result of split("\\|") on the last element in the array.
Also, there is no point in calling .toString() on a String object as you do in temporaryString = myArray[i].toString().
The code doesn't seem to be able to handle the possible expansion of strings in the secondary case. To make the code clearer, I would use a List rather than array.
private static String input = "5|12345|value1|value2|value3|value4+5|777|value1|value2|value3|value4?5|777|value1|value2|value3|value4+";
private void split(List<String> input, List<String> output, String split) {
for (String s: input) {
if (s.contains(split))
{
output.addAll(Arrays.asList(s.split(Pattern.quote(split)));
}
else
output.add(s);
}
}
public void mapObject(String input) {
List<String> inputSrings = new ArrayList<String>();
List<String> splitPlus = new ArrayList<String>();
inputStrings.add(input);
split(inputStrings, splitPlus);
List<String> splitQuest = new ArrayList<String>();
split(splitPlus, splitQuest, "?");
for (String s: splitQuest) {
// you can now set the attributes from the values in the list
// splitPipe
String[] attributes = s.split("\\|");
myObject.setAttribute1(attributes[0]);
....
myObject.setAttribute4(attributes[3]);
System.out.println(myObject);
}
}

Categories