8

I've posted about letters earlier, but this is an another topic, I have a json response that contain 2 objects, from and to , from is what to change, and to is what it will be changed to .

My code is :

// for example, the EnteredText is "ab b test a b" .
EnteredString = EnteredText.getText().toString();
for (int i = 0; i < m_jArry.length(); i++) {
    JSONObject jo_inside = m_jArry.getJSONObject(i);

    String Original = jo_inside.getString("from");
    String To = jo_inside.getString("to");

    if(isMethodConvertingIn){
        EnteredString = EnteredString.replace(" ","_");
        EnteredString = EnteredString.replace(Original,To + " ");
    } else {
        EnteredString = EnteredString.replace("_"," ");
        EnteredString = EnteredString.replace(To + " ", Original);
    }
}

LoadingProgress.setVisibility(View.GONE);
SetResultText(EnteredString);
ShowResultCardView();

For example, the json response is :

{
    "Response":[
        {"from":"a","to":"bhduh"},{"from":"b","to":"eieja"},{"from":"tes","to":"neesj"}
    ]
}

String.replace() method won't work here, because first it will replace a to bhduh, then b to eieja, BUT here's the problem, it will convert b in bhduh to eieja, which i don't want to.

I want to perfectly convert the letters and "words" in the String according the Json, but that what i'm failing at .

New Code :

if(m_jArry.length() > 0){
    HashMap<String, String> m_li;

    EnteredString = EnteredText.getText().toString();

    Log.i("TestAf_","Before Converting: "  + EnteredString);

    HashMap<String,String> replacements = new HashMap<String,String>();
    for (int i = 0; i < m_jArry.length(); i++) {
        JSONObject jo_inside = m_jArry.getJSONObject(i);

        String Original = jo_inside.getString("from");
        String To = jo_inside.getString("to");

        if(isMethodConvertingIn){

            //EnteredString = EnteredString.replace(" ","_");

            replacements.put(Original,To);
            Log.i("TestAf_","From: " + Original + " - To: " + To + " - Loop: " + i);
            //EnteredString = EnteredString.replace(" ","_");
            //EnteredString = EnteredString.replace(Original,To + " ");

        } else {

            EnteredString = EnteredString.replace("_"," ");
            EnteredString = EnteredString.replace("'" + To + "'", Original);
        }

    }
    Log.i("TestAf_","After Converting: " + replaceTokens(EnteredString,replacements));

    // Replace Logic Here
    // When Finish, Do :
    LoadingProgress.setVisibility(View.GONE);
    SetResultText(replaceTokens(EnteredString,replacements));
    ShowResultCardView();

Output :

10-10 19:51:19.757 12113-12113/? I/TestAf_: Before Converting: ab a ba
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: a - To: bhduh - Loop: 0
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: b - To: eieja - Loop: 1
10-10 19:51:19.757 12113-12113/? I/TestAf_: From: o - To: neesj - Loop: 2
10-10 19:51:19.758 12113-12113/? I/TestAf_: After Converting: ab a ba
Qix - MONICA WAS MISTREATED
  • 14,451
  • 16
  • 82
  • 145
Jaeger
  • 1,646
  • 8
  • 27
  • 59
  • do the conversions follow a specific pattern? or is it all arbitrary? – Technohacker Oct 10 '16 at 17:10
  • One solution would be to use some kind of markup for the replacement `{replacement}` for example, and replace step by step taking into account `{` and `}`, at the end, strip the delimiting `{}` –  Oct 10 '16 at 17:10
  • I think what @RC. is saying, is something like this http://stackoverflow.com/questions/959731/how-to-replace-a-set-of-tokens-in-a-java-string – OneCricketeer Oct 10 '16 at 17:12
  • I've posted a question about regex in my profile, i was thinking about adding `'` `'` befoe and after the changed letter, and when conveting the string, i will check if it's inside `'` or not, if YES then don't convert it, if NO then convert it, but this idea didn't work, because it was really complex to make, it worked on low count letters, but when writing whole words, it failed . – Jaeger Oct 10 '16 at 17:13
  • @cricket_007 thanks I was looking for an example ;) The idea is to insert delimiter to avoid replacement of replacement –  Oct 10 '16 at 17:13
  • @k3v1n No, it's based on the response, and not like what @ cricket_007 showed as an example. – Jaeger Oct 10 '16 at 17:14
  • It kinda is... You replace `a` with `[a]`, (brackets could be any characters) then you can follow along with that post, I think – OneCricketeer Oct 10 '16 at 17:15
  • Do I understand that you only want to change whole words? Would 'ab' get changed to 'bhduheieja' or would it stay 'ab'? – dcsohl Oct 10 '16 at 17:16
  • ab would change to eiejahduheieja, because there's a `b` in the first . – Jaeger Oct 10 '16 at 17:16
  • You just contradicted the question text... you complained that "it will replace `a` to `bhduh` ... but here's the problem, it will convert `b` in `bhduh` to `eieja`, which I don't want to." I'm confused now. – dcsohl Oct 10 '16 at 17:18
  • Hmm, you misunderstood, i want to change the letters/String in the EnteredString, what's happening is, it's replacing the letters/String in the EnteredString and Converted one, so `bhduh` is converted, i don't want to replace the converted one. – Jaeger Oct 10 '16 at 17:20
  • I'm asking what you *want* `ab` to turn into. – dcsohl Oct 10 '16 at 17:22
  • it should be `bhduheieja` – Jaeger Oct 10 '16 at 17:23
  • I've updated the code, I've used the provided code in @cricket_007 comment, after checking it didn't convert any letters/strings, I'm gonna update it with output to see where's the problem . – Jaeger Oct 10 '16 at 17:42
  • @AboHani I think I have a solution for you, but I would chat with you before to be sure it is the right solution. – Maytham Fahmi Oct 16 '16 at 13:11
  • Yeah sure, no problem. – Jaeger Oct 16 '16 at 14:06
  • It depends on how the `replaceTokens()` method has been implemented. Is it possible for you to post that code as well? – Shiraaz.M Oct 18 '16 at 06:07
  • I'm surprised nobody has mentioned this. If you're looking for speed, construct a [Trie](https://en.wikipedia.org/wiki/Trie) (prefix tree) from your result since your `from` values potentially have more than one character. This is _literally_ what they're for. – Qix - MONICA WAS MISTREATED Oct 18 '16 at 19:51
  • @Qix can you post an example ? – Jaeger Oct 18 '16 at 23:01
  • @Shiraz. M the method is in the answers, you can check it up, or check the link which was posted by cricket_007 – Jaeger Oct 18 '16 at 23:02

8 Answers8

4

You question would be clearer if you gave the expected output for the function.

Assuming it is: ab b test a b >>>> bhduheieja eieja neesjt bhduh eieja

then see the following, the key point in the Javadoc being "This will not repeat"

http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringUtils.html#replaceEach(java.lang.String,%20java.lang.String[],%20java.lang.String[])

Replaces all occurrences of Strings within another String. A null reference passed to this method is a no-op, or if any "search string" or "string to replace" is null, that replace will be ignored. This will not repeat. For repeating replaces, call the overloaded method.

Example 1

import org.apache.commons.lang3.StringUtils;

public class StringReplacer {

    public static void main(String[] args) {

        String input = "ab b test a b";
        String output = StringUtils.replaceEach(input, new String[] { "a", "b", "tes" },
                new String[] { "bhduh", "eieja", "neesj" });

        System.out.println(input + " >>>> " + output);
    }
}

Example 2

import org.apache.commons.lang3.StringUtils;

public class StringReplacer {

    public static void main(String[] args) {

        String input = "this is a test string with foo";
        String output = StringUtils.replaceEach(input, new String[] { "a", "foo" },
                new String[] { "foo", "bar"});

        System.out.println(input + " >>>> " + output);
    }
}
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
3

Try following:

Solution 1: Traverse the String characters one by one and move the new String to a new StringBuffer or StringBuilder, then call toString() to get the result. This will need you to implement string matching algorithm.

Solution 2 (Using Regex): For this, you must know the domain of your string. For example, it is [a-zA-Z] then other arbitrary characters (not part of domain) can be used for intermediate step. First replace the actual characters with arbitrary one then arbitrary ones with the target. In example below, [!@#] are the arbitrary characters. These can be any random \uxxxx value as well.

String input = "a-b-c";
String output = input.replaceAll("[a]", "!").replaceAll("[b]", "@").replaceAll("[c]", "#");
output = output.replaceAll("[!]", "bcd").replaceAll("[@]", "cde").replaceAll("[#]", "def");
System.out.println("input: " + input);
System.out.println("Expected: bcd-cde-def");
System.out.println("Actual: " + output);
Amber Beriwal
  • 1,568
  • 16
  • 30
  • Your solution 1 is the most robust. I hate seeing everyone jump to RegEx due to laziness. This is _not_ a good case for RegEx and _not_ a good (enough) reason to bring in Apache Commons if you're not already working with it. This is a perfect use-case for a [trie](https://en.wikipedia.org/wiki/Trie). – Qix - MONICA WAS MISTREATED Oct 18 '16 at 19:47
3

Your issue is quite common. To sum things up :

String test = "this is a test string with foo";
System.out.println(test.replace("a", "foo").replace("foo", "bar"));

Gives : this is bar test string with bar

Expected by you : this is foo test string with bar

You can use StrSubstitutor from Apache Commons Lang

But first you will have to inject placeholders in your string :

String test = "this is a test string with foo";

Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("a", "foo");
valuesMap.put("foo", "bar");
String testWithPlaceholder = test;
// Preparing the placeholders
for (String value : valuesMap.keySet())
{
    testWithPlaceholder = testWithPlaceholder.replace(value, "${"+value+"}");
}

And then, use StrSubstitutor

System.out.println(StrSubstitutor.replace(testWithPlaceholder, valuesMap));

It gives : this is foo test string with bar

ToYonos
  • 16,469
  • 2
  • 54
  • 70
2

Here is an method which is strictly just Java. I tried not to use any Java 8 methods here.

public static String translate(final String str, List<String> from, List<String> to, int index) {
    StringBuilder components = new StringBuilder();
    String token, replace;
    int p;

    if (index < from.size()) {
        token = from.get(index);
        replace = to.get(index);
        p = 0;

        for (int i = str.indexOf(token, p); i != -1; i = str.indexOf(token, p)) {
            if (i != p) {
                components.append(translate(str.substring(p, i), from, to, index + 1));
            }
            components.append(replace);
            p = i + token.length();
        }
        return components.append(translate(str.substring(p), from, to, index + 1)).toString();
    }

    return str;
}

public static String translate(final String str, List<String> from, List<String> to) {
    if (null == str) {
        return null;
    }
    return translate(str, from, to, 0);
}

Sample test program

public static void main(String []args) {
    String EnteredString = "aa  hjkyu  batesh  a";
    List<String> from = new ArrayList<>(Arrays.asList("a", "b", "tes"));
    List<String> to = new ArrayList<>(Arrays.asList("bhduh", "eieja", "neesj"));

    System.out.println(translate(EnteredString, from, to));
}

Output:

bhduhbhduh  hjkyu  eiejabhduhneesjh  bhduh

Explaination

The algorithm is recursive, and it simply does the following

  • If a pattern found in the string matches a pattern in the from list
    • if there is any string before that pattern, apply the algorithm to that string
    • replace the found pattern with the corresponding pattern in the to list
    • append the replacement to the new string
    • discard the pattern in the from list and repeat the algorithm for the rest of the string
  • Otherwise append the rest of the string to the new string
smac89
  • 39,374
  • 15
  • 132
  • 179
1

You could use split like: String[] pieces = jsonResponse.split("},{"); then you just parse the from and to in each piece and apply them with replace() then put the string back together again. (and please get your capitalization of your variables/methods right - makes it very hard to read the way you have it)

Brian Pipa
  • 808
  • 9
  • 23
1

Apache Commons StringUtils::replaceEach does this.

String[] froms = new String[] {"a", "b"};
String[] tos = new String[] {"b","c"};
String result = StringUtils.replaceEach("ab", froms, tos);
// result is "bc"
Daniel Rodriguez
  • 607
  • 6
  • 11
1

Why not keep it very simple (if the JSON is always in same format, EG: from the same system). Instead of replacing from with to, replace the entire markup:

replace "from":"*from*" with "from":"*to*"

Hugo Delsing
  • 13,803
  • 5
  • 45
  • 72
-3

Why not just change the actual "to" and "from" labels? That way, you don't run into a situation where "bhudh" becomes "eieja". Just do a string replace on "from" and "to".

Shadowman
  • 11,150
  • 19
  • 100
  • 198