Codewars 문제풀기 (03/30)
Your order, please
- String을 인자로 받는다.
- 문자열의 각 단어들은 1~9사이의 연속된 정수를 포함한다.
- 이 숫자들은 단어의 순서를 나타낸다.
- 순서대로 재배열하여 출력한다.
- 숫자가 없다면 출력 문자열에 포함하지 않는다.
- order(“is2 Thi1s T4est 3a”) –> “Thi1s is2 3a T4est”
- order(“4of Fo1r pe6ople g3ood th5e the2”) –> “Fo1r the2 g3ood 4of th5e pe6ople”
- order(“”) –> “”
- order(“Empty input should return empty string”) –> “”
1. Test를 만들었다.
-
입력 String에 number가 포함되어있지 않아 순서가 없을 때 ““(empty string)을 출력한다.
-
테스트 코드
@Test public void testShouldReturnEmptyStringWhenInputIsEmptyString() { // Given: Set string number empty String given = "Empty input should return empty string"; // When: Call order method String actual = Order.order(given); // Then: Should return empty string assertEquals("", actual); }
-
실제 코드
public class Order { public static String order(String words) { if (!words.matches("[0-9]")) { return ""; } return null; }
- 처음에 matches() 메서드를 사용할 때,
matches("[0-9]")
썼는데 이게 잘못된 것인지 몰랐다.
- 처음에 matches() 메서드를 사용할 때,
-
-
입력 String에 number가 포함되어 있고, (2-1-4-3) 순서 일 때, (1-2-3-4) 순서로 출력한다.
-
테스트 코드
@Test public void testShouldReturnNumberOrderIs1234WhenInputOrder2143() { // Given: Set string 2-1-4-3 order String given = "is2 Thi1s T4est 3a"; // When: Call order method String actual = Order.order(given); // Then: Should return 1-2-3-4 order string assertEquals("Thi1s is2 3a T4est", actual); }
-
실제 코드
public class Order { public static String order(String words) { if (!words.matches(".*[0-9].*")) { return ""; } String[] split = words.split(" "); String[] result = new String[split.length]; for(int i = 0; i<result.length; i++) { result[i] = ""; } for(String eachWord : split) { if (eachword.contains("1")) { result[0] = eachWord; } else if (eachWord.contains("2")) { result[1] = eachWord; } else if (eachWord.contains("3")) { result[2] = eachWord; } else if (eachWord.contains("4")) { result[3] = eachWord; } else if (eachWord.contaions("5")) { result[4] = eachWord; } else if (eachWord.contaions("6")) { result[5] = eachWord; } else if (eachWord.contaions("7")) { result[6] = eachWord; } else if (eachWord.contaions("8")) { result[7] = eachWord; } else if (eachWord.contaions("9")) { result[8] = eachWord; } } return String.join(" ", result).trim(); }
- 바로 생각나는 답안이 일단 코드가 매우 더럽지만, 각 문자를 space(“ “)로 쪼개서 1~9의 숫자가 포함되어 있을때 result라는 String 배열에 넣어서 join하는 방향으로 해결했다.
- 처음에 문자열에 숫자가 포함되어있는지 확인하는 부분에서 처음 테스트가 틀렸다는것을 알고 찾아보니
.*[0-9].*
이 숫자가 포함되어있는지 확인하는 정규식인것을 알게 되었다. - 일단 제출하긴 했는데 코드가 매우 더러워서 리팩토링을 시작했다.
-
2. 리팩토링
-
if / else문 말고 Stream써서 해결할 수 있을 것 같아서 코드를 좀 수정했다.
import java.util.Arrays; import java.util.stream.Collectors; public class Order { public static String order(String words) { String[] list = new String[10]; Arrays.fill(list, ""); for (int i = 1; i < 10; i++) { int finalI = i; String temp = Arrays.stream(words.split(" ")).filter(s -> s.contains(String.valueOf(finalI))) .collect(Collectors.joining()); list[i] = temp; } return String.join(" ", list).trim(); }
- 이렇게 하면 문자열에 숫자가 없는지 따로 체크할 필요가 없기 때문에 삭제했다.
- 제출하니까 통과하긴 했는데 만약에 연속된 숫자가 아니면 공백이 늘어나서 테스트 실패할것 같은데.. 라는 생각이 들었다. 일단 String에 포함된 1~9사이의 정수들은 연속된 숫자라고 했으니 상관없긴 했지만 좀 더 다른방법으로 수정할 수 있지 않을까 고민해봤다.
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Order { public static String order(String words) { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10; i++) { int finalI = i; String result = Arrays.stream(words.split(" ")).filter(s -> s.contains(String.valueOf(finalI))) .collect(Collectors.joining()); if (!result.equals("")) { list.add(result); } } return String.join(" ", list).trim(); } }
- 쪼갠 문자열에서 1~9가 포함되어 있다면 해당 문자열을 temp에 저장하고, 없다면 ““를 저장한다. 그래서 result가 ““가 아니라면 List에 순서대로 저장한다. List에 순서대로 저장하면 순서 숫자가 연속되어있지 않아도 공백이 늘어나지않아 괜찮다.
- 이대로 제출했다.
3. 답 비교, 느낀점
Best Practice 가장 많이 받은 코드
import java.util.Arrays;
import java.util.Comparator;
public class Order {
public static String order(String words) {
return Arrays.stream(words.split(" "))
.sorted(Comparator.comparing(s -> Integer.valueOf(s.replaceAll("\\D", ""))))
.reduce((a, b) -> a + " " + b).get();
}
}
- reduce() 전까지는 이해가 간다. 숫자외의 문자들을 전부 지우고, 남은 숫자 순서대로 정렬을 하는 것이다. 그런데 reduce()는 이해가 잘 안되었다. 직관적으로 보면 순서대로 정렬한 문자열을 붙여나가는 것 같은데.. reduce메서드를 잘 몰라서 모르겠다.
두번째로 많이 받은 코드
public class Order {
public static String order(String words) {
String[] arr = words.split(" ");
StringBuilder result = new StringBuilder("");
for (int i = 0; i < 10; i++) {
for (String s : arr) {
if (s.contains(String.valueOf(i))) {
result.append(s + " ");
}
}
}
return result.toString().trim();
}
}
-
이중 for문을 쓰긴 하지만, 간단한 구현이기 때문에 직관적으로 이해가 간다.
이 방법이 더 좋은거 같기도 하다..
모르는거 공부
-
정규표현식 2
- 문자열 포함여부 확인하기위해 보통 String의 contains()메서드를 사용한다.
- matches()메서드로 확인하는 방법
- matches(“.*<원하는문자열>.*");원하는문자열>
- ex) 문자열에 숫자가 포함되어있는지 확인하려면 –> matches(“.*[0-9].*”)
-
Stream의 reduce
-
Stream의 데이터를 변환하지 않고, 더하거나 빼는 등의 연산을 수행하여 하나의 값을 ㅗ만들 수 있다.
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Optional<Integer> sum = numbers.reduce((x, y) -> x + y);
-
stream의 1, 2를 더한 값 3을 다시 x에 할당하고 그 다음 값 3을 y에 할당한다.
- 1 + 2 –> (1+2) + 3 –> ((1+2) + 3) + 4 –> (((1+2)+3)+4) +5 –> 이런식..
- 위 문제에서
((a, b) -> a + " " + b)
는 숫자 순서대로 정렬된 stream을 차례로 돌면서 문자열을 붙여나가는것이다.
-
-
댓글남기기