Matching patterns in List of List in java - java

I have a ArrayList of strings LinkSet which contains the following items
["0,3","0,13","0,28","12,3","13,3","28,12"]
I have another list of list pattern2 with the following containing
[[0,3], [0,13,3], [0,28,12,3]]
I would like to match each element pattern of the list LinkSet in my list pattern2 and replace the element found by the position of the match from LinkSet. As a result, I would like to have a list of list with like :
[[0],[1,4],[2,5,3]]
From this new list of list, 0 is the position of the "0,3" from the original list,1 is the position of 0,13 in the original list and so on.
I tried this:
String pattern2="";
for (int k=0; k<graph.LinkSet.size();k++)
{
String temp=""
for(int m=0;m<pattern.size();m++)
{
temp=pattern.get(m).toString();
if (temp.contains("[["+LinkSet.get(k)+"],"))
{
pattern=pattern+"[["+k+"],";
}
else if (temp.contains("["+LinkSet.get(k)+"],"))
{
pattern=pattern+"["+k+"],";
}
else if (temp.contains(", ["+LinkSet.get(k)))
{
pattern=pattern+", ["+k+",";
}
else if (temp.contains(", ["+LinkSet.get(k)))
{
pattern=pattern+", ["+k+",";
}
}
}
//System.out.println("after"+temp);
System.out.println("pattern"+pattern2);
But it does not give me what I would like to have. It gives me
,[,[[[1,2,3],
it seems to overwrite pattern2 for each loop

Looks like the problem is, that you're working on "pattern" and change it inside the innner-loop instead of storing your results in pattern2... But that doesn't explain your results. Also you're messing around with the brackets unnecessarily. And you need to switch the loops to get the result you want.
How about this:
String result = "";
// Loop over pattern
for (int m = 0; m < pattern.size(); m++) {
String patternResult = "";
String patternToBeMatched = pattern.get(m);
// Loop over source
for (int k=0; k<graph.LinkSet.size(); k++) {
String toAnalyse = graph.LinkSet.get(k);
if (toAnalyse.contains(patternToBeMatched){
patternResult = patternResult + k + ", ";
}
}
// Remove additional ", " if not empty and set brackets
if (patternResult.length() > 0){
patternResult = "[" + patternResult.substring(0, patternResult.length() - 2) + "]";
}
result = result + patternResult;
}
result = "[" + result + "]";

Related

Check for multiple occurrence of certain character in string

Edit: To those who downvote me, this question is difference from the duplicate question which you guy linked. The other question is about returning the indexes. However, for my case, I do not need the index. I just want to check whether there is duplicate.
This is my code:
String word = "ABCDE<br>XYZABC";
String[] keywords = word.split("<br>");
for (int index = 0; index < keywords.length; index++) {
if (keywords[index].toLowerCase().contains(word.toLowerCase())) {
if (index != (keywords.length - 1)) {
endText = keywords[index];
definition.setText(endText);
}
}
My problem is, if the keywords is "ABC", then the string endText will only show "ABCDE". However, "XYZABC" contains "ABC" as well. How to check if the string has multiple occurrence? I would like to make the definition textview become definition.setText(endText + "More"); if there is multiple occurrence.
I tried this. The code is working, but it is making my app very slow. I guess the reason is because I got the String word through textwatcher.
String[] keywords = word.split("<br>");
for (int index = 0; index < keywords.length; index++) {
if (keywords[index].toLowerCase().contains(word.toLowerCase())) {
if (index != (keywords.length - 1)) {
int i = 0;
Pattern p = Pattern.compile(search.toLowerCase());
Matcher m = p.matcher( word.toLowerCase() );
while (m.find()) {
i++;
}
if (i > 1) {
endText = keywords[index];
definition.setText(endText + " More");
} else {
endText = keywords[index];
definition.setText(endText);
}
}
}
}
Is there any faster way?
It's a little hard for me to understand your question, but it sounds like:
You have some string (e.g. "ABCDE<br>XYZABC"). You also have some target text (e.g. "ABC"). You want to split that string on a delimiter (e.g. "<br>", and then:
If exactly one substring contains the target, display that substring.
If more than one substring contains the target, display the last substring that contains it plus the suffix "More"
In your posted code, the performance is really slow because of the Pattern.compile() call. Re-compiling the Pattern on every loop iteration is very costly. Luckily, there's no need for regular expressions here, so you can avoid that problem entirely.
String search = "ABC".toLowerCase();
String word = "ABCDE<br>XYZABC";
String[] keywords = word.split("<br>");
int count = 0;
for (String keyword : keywords) {
if (keyword.toLowerCase().contains(search)) {
++count;
endText = keyword;
}
}
if (count > 1) {
definition.setText(endText + " More");
}
else if (count == 1) {
definition.setText(endText);
}
You are doing it correctly but you are doing unnecessary check which is if (index != (keywords.length - 1)). This will ignore if there is match in the last keywords array element. Not sure is that a part of your requirement.
To enhance performance when you found the match in second place break the loop. You don't need to check anymore.
public static void main(String[] args) {
String word = "ABCDE<br>XYZABC";
String pattern = "ABC";
String[] keywords = word.split("<br>");
String endText = "";
int count = 0;
for (int index = 0; index < keywords.length; index++) {
if (keywords[index].toLowerCase().contains(pattern.toLowerCase())) {
//If you come into this part mean found a match.
if(count == 1) {
// When you found the second match it will break to loop. No need to check anymore
// keep the first found String and append the more part to it
endText += " more";
break;
}
endText = keywords[index];
count++;
}
}
System.out.println(endText);
}
This will print ABCDE more
Hi You have to use your condition statement like this
if (word.toLowerCase().contains(keywords[index].toLowerCase()))
You can use this:
String word = "ABCDE<br>XYZABC";
String[] keywords = word.split("<br>");
for (int i = 0; i < keywords.length - 1; i++) {
int c = 0;
Pattern p = Pattern.compile(keywords[i].toLowerCase());
Matcher m = p.matcher(word.toLowerCase());
while (m.find()) {
c++;
}
if (c > 1) {
definition.setText(keywords[i] + " More");
} else {
definition.setText(keywords[i]);
}
}
But like what I mentioned in comment, there is no double occurrence in word "ABCDE<br>XYZABC" when you want to split it by <br>.
But if you use the word "ABCDE<br>XYZABCDE" there is two occurrence of word "ABCDE"
void test() {
String word = "ABCDE<br>XYZABC";
String sequence = "ABC";
if(word.replaceFirst(sequence,"{---}").contains(sequence)){
int startIndex = word.indexOf(sequence);
int endIndex = word.indexOf("<br>");
Log.v("test",word.substring(startIndex,endIndex)+" More");
}
else{
//your code
}
}
Try this

add space between list elements at odd position

I have a string:
str = "Hello there"
I am removing the whitespace:
String[] parts = str.split("\\s+");
Creating a List and populating it with the parts:
List<String> theParts = new ArrayList<String>();
for (int i = 0; i < parts.length; i++) {
theParts.add(parts[i]);
}
The size of the List is 2.Now, I want to increase it's size in order to be the same size as another list.
Let's say the other list has size 3.
So, I check:
if (otherList.size() > theParts.size()) {
and then, I want to change the theParts list in order to contain an empty space (the number which shows how much greater the otherList is) between it's parts.
So, I want theParts to be (add a space at every odd position):
theParts[0] = "Hello"
theParts[1] = " "
theParts[2] = "there"
I am not sure if this can be happen with Lists, but I can't think another solution.
Or use something like join (doesn't work, just an idea to use something like this):
if (otherList.size() > theParts.size()) {
for (int i = 0; i < otherList.size(); i++) {
if (i%2 !=0) {
String.join(" ", theParts);
}
}
}
Just insert the spaces as you're populating the list:
List<String> theParts = new ArrayList<>(2 * parts.length - 1);
for (int i = 0; i < parts.length; i++) {
if (i > 0) theParts.add(" ");
theParts.add(parts[i]);
}
You could use a word break regex:
public void test() throws Exception {
String str = "Hello there";
List<String> strings = Arrays.asList(str.split("\\b"));
for ( String s : strings ) {
System.out.println("'"+s+"'");
}
}
this will retain all of the spaces for you.
'Hello'
' '
'there'
for(String dis : theParts){
newParts.add(dis);//'newPart is another list '
String last = parts[parts.length -2]; // until new list read last element
if(!last.equals(dis)){
newParts.add(" ");
}if(last.equals(dis)){
newParts.add(" ");
}
}

Issue iterating through two arraylists

EDIT: Thanks so much for all the really quick feedback. Wow. I did just paste it all for you instead of just those two for loops. Thanks.
This may have been totally answered before. I have read SO for the last few years but this is my first post. I have been using the site and others to help solve this so my apologies in advance if this has been answered!
I am iterating through two arraylists. One is derived from user input; the other is a dictionary file converted into an arraylist. I am trying to compare a word in the input with a dictionary word. The input list and the dictionary list are valid and if I simply iterate through them, they contain what they should (so that isn't the issue. I assume my issue is somewhere with how I am handling the iteration. I'm a fairly novice Java programmer so please go easy on me.
Thanks
public String isSub(String x) throws FileNotFoundException, IOException {
//todo handle X
String out = "**********\nFor input \n" + x + "If you're reading this no match was found.\n**********";
String dictionary;
boolean solve = true;
/// Get dictionary
dictMaker newDict = new dictMaker();
dictionary = newDict.arrayMaker();
List<String> myDict = new ArrayList<String>(Arrays.asList(dictionary.split(",")));
List<String> input = new ArrayList<String>(Arrays.asList(x.split(" ")));
List<String> results = new ArrayList<String>();
//results = input;
String currentWord;
String match = "";
String checker = "";
String fail="";
//Everything to break sub needs to happen here.
while (solve) {
for(int n = 0; n < input.size(); n++) { //outside FOR (INPUT)
if(!fail.equals("")) results.add(fail);
checker = input.get(n).trim();
for(int i = 0; i < myDict.size(); i++) { //inside FOR (dictionary)
currentWord = myDict.get(i).trim();
System.out.print(checker + " " + currentWord + "\n");
if(checker.equals(currentWord)) {
match = currentWord;
results.add(currentWord);
fail="";
} //end if
else {
fail = "No match for " + checker;
}
}//end inside FOR (dictionary)
} //END OUTSIDE FOR (input)
solve=false;
} //end while
out = results.toString();
return out;
}
Output results for input "test tester asdasdfasdlfk"
[test, No match for test, tester, No match for tester]
Carl Manaster gave the correct explanation.
Here's an improved version of your code:
for (int n = 0; n < input.size(); n++) { //outside FOR (INPUT)
String checker = input.get(n).trim();
boolean match = false;
for (int i = 0; i < myDict.size(); i++) { //inside FOR (dictionary)
String currentWord = myDict.get(i).trim();
System.out.print(checker + " " + currentWord + "\n");
if (checker.equals(currentWord)) {
match = true;
results.add(currentWord);
break;
} //end if
} //end inside FOR (dictionary)
if (!match) {
results.add("No match for " + checker);
}
} //END OUTSIDE FOR (input)
Also, consider using a HashMap instead of an ArrayList to store the dictionary and trim the words when you store them to avoid doing it in each pass.
It looks as though every word in input gets compared to every word in your dictionary. So for every word that doesn't match, you get a fail (although you only write the last failure in the dictionary to the results). The problem appears to be that you keep looping even after you have found the word. To avoid this, you probably want to add break to the success case:
if (checker.equals(currentWord)) {
match = currentWord;
results.add(currentWord);
fail = "";
break;
} else {
fail = "No match for " + checker;
}
If you are using a dictionary, you should get it with keys not with index. So it should be
if(myDict.containsKey(checker)){
String currentWord =myDict.get(checker);
System.out.print(checker + " " + currentWord + "\n");
match = currentWord;
results.add(currentWord);
fail = "";
}
else {
fail = "No match for " + checker;
}
I think more or less your code should like following.
ArrayList<String> input= new ArrayList<String>();
input.add("ahmet");
input.add("mehmet");
ArrayList<String> results= new ArrayList<String>();
Map<String, String> myDict = new HashMap<String, String>();
myDict.put("key", "ahmet");
myDict.put("key2", "mehmet");
String match="";
String fail="";
for (int n = 0; n < input.size(); n++) { //outside FOR (INPUT)
if (!fail.equals(""))
results.add(fail);
String checker = input.get(n).trim();
for (int i = 0; i < myDict.size(); i++) { //inside FOR (dictionary)
// String currentWord = myDict.get(i).trim();
if(myDict.containsKey(checker)){
String currentWord =myDict.get(checker);
System.out.print(checker + " " + currentWord + "\n");
match = currentWord;
results.add(currentWord);
fail = "";
}
else {
fail = "No match for " + checker;
}
} // end inside FOR (dictionary)
} // end outside FOR (input)
// solve = false; I dont know what is this
//} //end while. no while in my code
return results.toString();
You should place the dictionary to a HashSet and trim while add all words. Next you just need to loop the input list and compare with dict.conatins(inputWord). This saves the possible huge dictionary loop processed for all input words.
Untested brain dump:
HashSet<String> dictionary = readDictionaryFiles(...);
List<String> input = getInput();
for (String inputString : input)
{
if (dictionary.contains(inputString.trim()))
{
result.add(inputString);
}
}
out = result.toString()
....
And a solution similar to the original posting. The unnecessary loop index variables are removed:
for (String checker : input)
{ // outside FOR (INPUT)
fail = "No match for " + checker;
for (String currentWord : myDict)
{ // inside FOR (dictionary)
System.out.print(checker + " " + currentWord + "\n");
if (checker.equals(currentWord))
{
match = currentWord;
results.add(currentWord);
fail = null;
break;
}
} // end inside FOR (dictionary)
if (fail != null)
{
results.add(fail);
}
} // end outside FOR (input)
solve = false;
return results.toString();
The trim should be made while add the elements to the list. Trim the dictionary values each time is overhead. And the inner loop itself too. The complexity of the task can be reduced if the dictionary data structure is changed from List to Set.
Adding the result of "fail" is moved to the end of the outer loop. Otherwise the result of the last input string is not added to the result list.
The following code is terrible:
else {
fail = "No match for " + checker;
}
The checker does not change within the dictionary loop. But the fail string is constructed each time the checker and the dictionary value does not match.

How to extract a multiple quoted substrings in Java

I have a string that has multiple substring which has to be extracted. Strings which will be extracted is between ' character.
I could only extract the first or the last one when I use indexOf or regex.
How could I extract them and put them into array or list without parsing the same string only?
resultData = "Error 205: 'x' data is not crawled yet. Check 'y' and 'z' data and update dataset 't'";
I have a tried below;
protected static String errorsTPrinted(String errStr, int errCode) {
if (errCode== 202 ) {
ArrayList<String> ar = new ArrayList<String>();
Pattern p = Pattern.compile("'(.*?)'");
Matcher m = p.matcher(errStr);
String text;
for (int i = 0; i < errStr.length(); i++) {
m.find();
text = m.group(1);
ar.add(text);
}
return errStr = "Err 202: " + ar.get(0) + " ... " + ar.get(1) + " ..." + ar.get(2) + " ... " + ar.get(3);
}
Edit
I used #MinecraftShamrock 's approach.
if (errCode== 202 ) {
List<String> getQuotet = getQuotet(errStr, '\'');
return errStr = "Err 202: " + getQuotet.get(0) + " ... " + getQuotet.get(1) + " ..." + getQuotet.get(2) + " ... " + getQuotet.get(3);
}
You could use this very straightforward algorithm to do so and avoid regex (as one can't be 100% sure about its complexity):
public List<String> getQuotet(final String input, final char quote) {
final ArrayList<String> result = new ArrayList<>();
int n = -1;
for(int i = 0; i < input.length(); i++) {
if(input.charAt(i) == quote) {
if(n == -1) { //not currently inside quote -> start new quote
n = i + 1;
} else { //close current quote
result.add(input.substring(n, i));
n = -1;
}
}
}
return result;
}
This works with any desired quote-character and has a runtime complexity of O(n). If the string ends with an open quote, it will not be included. However, this can be added quite easily.
I think this is preferable over regex as you can ba absolutely sure about its complexity. Also, it works with a minimum of library classes. If you care about efficiency for big inputs, use this.
And last but not least, it does absolutely not care about what is between two quote characters so it works with any input string.
Simply use the pattern:
'([^']++)'
And a Matcher like so:
final Pattern pattern = Pattern.compile("'([^']++)'");
final Matcher matcher = pattern.matcher(resultData);
while (matcher.find()) {
System.out.println(matcher.group(1));
}
This loops through each match in the String and prints it.
Output:
x
y
z
t
Here is a simple approach (assuming there are no escaping characters etc.):
// Compile a pattern to find the wanted strings
Pattern p = Pattern.compile("'([^']+)'");
// Create a matcher for given input
Matcher m = p.matcher(resultData);
// A list to put the found strings into
List<String> list = new ArrayList<String>();
// Loop over all occurrences
while(m.find()) {
// Retrieve the matched text
String text = m.group(1);
// Do something with the text, e.g. add it to a List
list.add(text);
}

Best Loop Idiom for special casing the last element

I run into this case a lot of times when doing simple text processing and print statements where I am looping over a collection and I want to special case the last element (for example every normal element will be comma separated except for the last case).
Is there some best practice idiom or elegant form that doesn't require duplicating code or shoving in an if, else in the loop.
For example I have a list of strings that I want to print in a comma separated list. (the do while solution already assumes the list has 2 or more elements otherwise it'd be just as bad as the more correct for loop with conditional).
e.g. List = ("dog", "cat", "bat")
I want to print "[dog, cat, bat]"
I present 2 methods the
For loop with conditional
public static String forLoopConditional(String[] items) {
String itemOutput = "[";
for (int i = 0; i < items.length; i++) {
// Check if we're not at the last element
if (i < (items.length - 1)) {
itemOutput += items[i] + ", ";
} else {
// last element
itemOutput += items[i];
}
}
itemOutput += "]";
return itemOutput;
}
do while loop priming the loop
public static String doWhileLoopPrime(String[] items) {
String itemOutput = "[";
int i = 0;
itemOutput += items[i++];
if (i < (items.length)) {
do {
itemOutput += ", " + items[i++];
} while (i < items.length);
}
itemOutput += "]";
return itemOutput;
}
Tester class:
public static void main(String[] args) {
String[] items = { "dog", "cat", "bat" };
System.out.println(forLoopConditional(items));
System.out.println(doWhileLoopPrime(items));
}
In the Java AbstractCollection class it has the following implementation (a little verbose because it contains all edge case error checking, but not bad).
public String toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}
I usually write it like this:
static String commaSeparated(String[] items) {
StringBuilder sb = new StringBuilder();
String sep = "";
for (String item: items) {
sb.append(sep);
sb.append(item);
sep = ",";
}
return sb.toString();
}
There are a lot of for loops in these answers, but I find that an Iterator and while loop reads much more easily. E.g.:
Iterator<String> itemIterator = Arrays.asList(items).iterator();
if (itemIterator.hasNext()) {
// special-case first item. in this case, no comma
while (itemIterator.hasNext()) {
// process the rest
}
}
This is the approach taken by Joiner in Google collections and I find it very readable.
string value = "[" + StringUtils.join( items, ',' ) + "]";
My usual take is to test if the index variable is zero, e.g.:
var result = "[ ";
for (var i = 0; i < list.length; ++i) {
if (i != 0) result += ", ";
result += list[i];
}
result += " ]";
But of course, that's only if we talk about languages that don't have some Array.join(", ") method. ;-)
I think it is easier to think of the first element as the special case because it is much easier to know if an iteration is the first rather than the last. It does not take any complex or expensive logic to know if something is being done for the first time.
public static String prettyPrint(String[] items) {
String itemOutput = "[";
boolean first = true;
for (int i = 0; i < items.length; i++) {
if (!first) {
itemOutput += ", ";
}
itemOutput += items[i];
first = false;
}
itemOutput += "]";
return itemOutput;
}
I'd go with your second example, ie. handle the special case outside of the loop, just write it a bit more straightforward:
String itemOutput = "[";
if (items.length > 0) {
itemOutput += items[0];
for (int i = 1; i < items.length; i++) {
itemOutput += ", " + items[i];
}
}
itemOutput += "]";
Java 8 solution, in case someone is looking for it:
String res = Arrays.stream(items).reduce((t, u) -> t + "," + u).get();
I like to use a flag for the first item.
ArrayList<String> list = new ArrayList()<String>{{
add("dog");
add("cat");
add("bat");
}};
String output = "[";
boolean first = true;
for(String word: list){
if(!first) output += ", ";
output+= word;
first = false;
}
output += "]";
Since your case is simply processing text, you don't need the conditional inside the loop. A C example:
char* items[] = {"dog", "cat", "bat"};
char* output[STRING_LENGTH] = {0};
char* pStr = &output[1];
int i;
output[0] = '[';
for (i=0; i < (sizeof(items) / sizeof(char*)); ++i) {
sprintf(pStr,"%s,",items[i]);
pStr = &output[0] + strlen(output);
}
output[strlen(output)-1] = ']';
Instead of adding a conditional to avoid generating the trailing comma, go ahead and generate it (to keep your loop simple and conditional-free) and simply overwrite it at the end. Many times, I find it clearer to generate the special case just like any other loop iteration and then manually replace it at the end (although if the "replace it" code is more than a couple of lines, this method can actually become harder to read).
...
String[] items = { "dog", "cat", "bat" };
String res = "[";
for (String s : items) {
res += (res.length == 1 ? "" : ", ") + s;
}
res += "]";
or so is quite readable. You can put the conditional in a separate if clause, of course. What it makes idiomatic (I think so, at least) is that it uses a foreach loop and does not use a complicated loop header.
Also, no logic is duplicated (i.e. there is only one place where an item from items is actually appended to the output string - in a real world application this might be a more complicated and lengthy formatting operation, so I wouldn't want to repeat the code).
In this case, you are essentially concatenating a list of strings using some separator string. You can maybe write something yourself which does this. Then you will get something like:
String[] items = { "dog", "cat", "bat" };
String result = "[" + joinListOfStrings(items, ", ") + "]"
with
public static String joinListOfStrings(String[] items, String sep) {
StringBuffer result;
for (int i=0; i<items.length; i++) {
result.append(items[i]);
if (i < items.length-1) buffer.append(sep);
}
return result.toString();
}
If you have a Collection instead of a String[] you can also use iterators and the hasNext() method to check if this is the last or not.
If you are building a string dynamically like that, you shouldn't be using the += operator.
The StringBuilder class works much better for repeated dynamic string concatenation.
public String commaSeparate(String[] items, String delim){
StringBuilder bob = new StringBuilder();
for(int i=0;i<items.length;i++){
bob.append(items[i]);
if(i+1<items.length){
bob.append(delim);
}
}
return bob.toString();
}
Then call is like this
String[] items = {"one","two","three"};
StringBuilder bob = new StringBuilder();
bob.append("[");
bob.append(commaSeperate(items,","));
bob.append("]");
System.out.print(bob.toString());
Generally, my favourite is the multi-level exit. Change
for ( s1; exit-condition; s2 ) {
doForAll();
if ( !modified-exit-condition )
doForAllButLast();
}
to
for ( s1;; s2 ) {
doForAll();
if ( modified-exit-condition ) break;
doForAllButLast();
}
It eliminates any duplicate code or redundant checks.
Your example:
for (int i = 0;; i++) {
itemOutput.append(items[i]);
if ( i == items.length - 1) break;
itemOutput.append(", ");
}
It works for some things better than others. I'm not a huge fan of this for this specific example.
Of course, it gets really tricky for scenarios where the exit condition depends on what happens in doForAll() and not just s2. Using an Iterator is such a case.
Here's a paper from the prof that shamelessly promoted it to his students :-). Read section 5 for exactly what you're talking about.
I think there are two answers to this question: the best idiom for this problem in any language, and the best idiom for this problem in java. I also think the intent of this problem wasn't the tasks of joining strings together, but the pattern in general, so it doesn't really help to show library functions that can do that.
Firstly though the actions of surrounding a string with [] and creating a string separated by commas are two separate actions, and ideally would be two separate functions.
For any language, I think the combination of recursion and pattern matching works best. For example, in haskell I would do this:
join [] = ""
join [x] = x
join (x:xs) = concat [x, ",", join xs]
surround before after str = concat [before, str, after]
yourFunc = surround "[" "]" . join
-- example usage: yourFunc ["dog", "cat"] will output "[dog,cat]"
The benefit of writing it like this is it clearly enumerates the different situations that the function will face, and how it will handle it.
Another very nice way to do this is with an accumulator type function. Eg:
join [] = ""
join strings = foldr1 (\a b -> concat [a, ",", b]) strings
This can be done in other languages as well, eg c#:
public static string Join(List<string> strings)
{
if (!strings.Any()) return string.Empty;
return strings.Aggregate((acc, val) => acc + "," + val);
}
Not very efficient in this situation, but can be useful in other cases (or efficiency may not matter).
Unfortunately, java can't use either of those methods. So in this case I think the best way is to have checks at the top of the function for the exception cases (0 or 1 elements), and then use a for loop to handle the case with more than 1 element:
public static String join(String[] items) {
if (items.length == 0) return "";
if (items.length == 1) return items[0];
StringBuilder result = new StringBuilder();
for(int i = 0; i < items.length - 1; i++) {
result.append(items[i]);
result.append(",");
}
result.append(items[items.length - 1]);
return result.toString();
}
This function clearly shows what happens in the two edge cases (0 or 1 elements). It then uses a loop for all but the last elements, and finally adds the last element on without a comma. The inverse way of handling the non-comma element at the start is also easy to do.
Note that the if (items.length == 1) return items[0]; line isn't actually necessary, however I think it makes what the function does more easier to determine at a glance.
(Note that if anyone wants more explanation on the haskell/c# functions ask and I'll add it in)
It can be achieved using Java 8 lambda and Collectors.joining() as -
List<String> items = Arrays.asList("dog", "cat", "bat");
String result = items.stream().collect(Collectors.joining(", ", "[", "]"));
System.out.println(result);
I usually write a for loop like this:
public static String forLoopConditional(String[] items) {
StringBuilder builder = new StringBuilder();
builder.append("[");
for (int i = 0; i < items.length - 1; i++) {
builder.append(items[i] + ", ");
}
if (items.length > 0) {
builder.append(items[items.length - 1]);
}
builder.append("]");
return builder.toString();
}
If you are just looking for a comma seperated list of like this: "[The, Cat, in, the, Hat]", don't even waste time writing your own method. Just use List.toString:
List<String> strings = Arrays.asList("The", "Cat", "in", "the", "Hat);
System.out.println(strings.toString());
Provided the generic type of the List has a toString with the value you want to display, just call List.toString:
public class Dog {
private String name;
public Dog(String name){
this.name = name;
}
public String toString(){
return name;
}
}
Then you can do:
List<Dog> dogs = Arrays.asList(new Dog("Frank"), new Dog("Hal"));
System.out.println(dogs);
And you'll get:
[Frank, Hal]
A third alternative is the following
StringBuilder output = new StringBuilder();
for (int i = 0; i < items.length - 1; i++) {
output.append(items[i]);
output.append(",");
}
if (items.length > 0) output.append(items[items.length - 1]);
But the best is to use a join()-like method. For Java there's a String.join in third party libraries, that way your code becomes:
StringUtils.join(items,',');
FWIW, the join() method (line 3232 onwards) in Apache Commons does use an if within a loop though:
public static String join(Object[] array, char separator, int startIndex, int endIndex) {
if (array == null) {
return null;
}
int bufSize = (endIndex - startIndex);
if (bufSize <= 0) {
return EMPTY;
}
bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + 1);
StringBuilder buf = new StringBuilder(bufSize);
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
if (array[i] != null) {
buf.append(array[i]);
}
}
return buf.toString();
}

Categories

Resources