2016-07-07 1 views
3

Так что я работаю над проектом, в котором мне нужно преобразовать строку, содержащую греческие буквы, в строку, содержащую английское представление для этой буквы. Таким образом, греческий α стал бы alpha и Α стал бы Alpha.Использование выражения Lambda для замены символов в строке с использованием HashMap

Я создал HashMap, который имеет соответствующие преобразования из Unicode Character в обычную строку.

Я использовал простой For цикл, чтобы проверить, если на входе StringChar находится в HashMap в Key и если после этого я буду добавлять его замену, используя StringBuilder. Вот код, я использую, чтобы сделать это:

char[] ca = snippet.toCharArray(); 
     StringBuilder out = new StringBuilder(); 
     for (char c : ca) { 
      if (GREEK_LETTER_DICT.containsKey(Character.toString(c))) { 
       out.append(GREEK_LETTER_DICT.get(Character.toString(c))); 
      } else { 
       out.append(c); 
      } 
     } 
    return out.toString(); 

Это в общественном статический метод с String в качестве входных данных. Вещь, которую я хочу знать, это если можно сделать то же самое с выражением лямбда? Я уже нашел пару решений, заменяющих Character в String, но они не используют HashMap/Dictionary.

Я понимаю, что совершенно бесполезно просто преобразовать это в выражение лямбда ради использования выражения лямбда, но поскольку у меня есть еще 7 из этих функций, которые сокращают мой код почти на 60%, я хотел бы видеть, это возможно сделать в «одной строке» кода. Таким образом, я могу уменьшить количество отдельных методов, которые я использую, и получить более чистый код.

До сих пор я нашел способ преобразовать String к Stream с помощью:

String.chars() 

Тогда преобразование этого IntStream в Stream<Character> с

.mapToObj(ch -> (char) ch) 

и фильтрации, чтобы увидеть, если Character является в HashMap с

.filter(ch -> (GREEK_LETTER_DICT.containsKey(ch))) 

Проблема заключается в том, что я не в состоянии

  1. добавить оригинальный Character к выходу String, если он не в HashMap
  2. заменить Character с строки в HashMap

Так что любая помощь по этим двум пунктам оценивается. Я узнал, что иногда я считаю неправильным, потому что вместо того, чтобы видеть, что Character в String равен Character в списке параметров, я должен был проверить, возвращает ли этот список параметров положительный index.Так что это (PseudeoCode):

"StringWithOptions".indexOf('characterLiteral')

вместо этого:

Character.equals("One|of|these")

+3

на основе кода в вопросительных 'str.chars() Foreach (символ -> out.append (GREEK_LETTER_DICT.getOrDefault (Character.toString (с), с))),' – SMA

+0

Ну это было быстро; -) Спасибо за ответ. с этим кодом удалось заставить его работать. Я поставлю его здесь. Мне пришлось внести несколько изменений в код. –

+0

@SMA красивый. Я бы посоветовал вам ответить на этот вопрос. – Mena

ответ

2

Я хотел бы сделать что-то вроде этого:

str 
    .chars() 
    .forEach(
     character -> out.append(
      GREEK_LETTER_DICT.getOrDefault(Character.toString(c), c) 
     ) 
    ); 
+1

не 'map' лучше? OP тогда вообще не понадобится StringBuilder? – niceman

+1

Это просто повод использовать потоки. Используйте нормальный для каждого цикла, если он будет состоять из состояния. – 4castle

1

Таким образом, используя код из SMA и Martin Seeler Я смог заставить его работать. Полученный код теперь возвращает String с правильными результатами и передает все мои модульные тесты.

Это результирующий код:

return snippet.chars() 
      .mapToObj(c -> (char) c) 
      .collect(Collector.of(StringBuilder::new, 
        (stringBuilder, c) -> stringBuilder.append(GREEK_LETTER_DICT.getOrDefault(c,c.toString())), 
        StringBuilder::append, 
        StringBuilder::toString)); 

Кодекс от SMA была одна проблема, где HashMap использует <Character, String> и не <String, String>. Поэтому мне пришлось изменить параметры #getOrDefault. Сочетание этого с .mapToObj(c -> (char) c) сделал трюк.

Теперь давайте посмотрим, если я могу изменить это:

(stringBuilder, c) -> stringBuilder.append(GREEK_LETTER_DICT.getOrDefault(c,c.toString())), 
         StringBuilder::append 

к чему-то вроде StringBuilder::append(GREEK_LETTER_DICT.getOrDefault(c,Character.toString(c))

+0

... вы считаете это короче/проще, чем ваш исходный цикл? – Holger

4

Как о том, что:

public static String replaceGreekLetters(String snippet) { 
    return snippet 
      .chars() 
      .mapToObj(c -> (char) c) 
      .map(c -> GREEK_LETTER_DICT.getOrDefault(c, c.toString())) 
      .collect(Collectors.joining()); 
} 
+0

это лучше, чем манипулировать переменной состояния (Stringbuilder), она обеспечивает лучший параллелизм :) – niceman

+0

@niceman Фильтр для этого может быть выполнен параллельно. вы бы порекомендовали не использовать «StringBuilder»? –

+0

@NathanvanDalen yes Я бы – niceman

2

Есть несколько хороших решений с использованием парит. Мне нравится более простые больше: преобразование

  • избегает из полукокса в строку
  • использования
  • избежать из StringBuffer вне потока pipline
  • использования предопределенного Коллектора
public class ReplaceGreek { 

    private Map<String, String> DICT = new HashMap<>(); 
    { 
     DICT.put("α", "alpha"); 
    } 

    public String replace(final String original) { 
     return Arrays.stream(original.split("")) 
       .map(c -> DICT.getOrDefault(c, c)) 
       .collect(Collectors.joining()); 
    } 
} 

и даже работает! .

public class ReplaceGreekTest { 

    @Test 
    public void test() { 
     ReplaceGreek rg = new ReplaceGreek(); 
     String greek = "This is greek: α"; 

     String nogreek = rg.replace(greek); 

     assertEquals("This is greek: alpha", nogreek); 
    } 

} 
+0

OP использует «Map ». Хорошее решение, хотя! – 4castle

+0

Но эта тема безопасна? Я думаю, это потому, что сам 'String'. Это действительно очень элегантное решение с тремя строками кода. Большая разница в читаемости по сравнению с моими 10 строками ранее. @ 4castle: я могу легко вернуться к ''. –

+0

@NathanvanDalen Да, это потокобезопасный. Это очень похоже на другое решение. – 4castle

Смежные вопросы