Breadcrumb Generator, CodeWars Kata
Breadcrumb Generator
As breadcrumb menùs are quite popular today, I won't digress much on explaining them, leaving the wiki link to do all the dirty work in my place.
What might not be so trivial is instead to get a decent breadcrumb from your current url. For this kata, your purpose is to create a function that takes a url, strips the first part (labelling it always HOME
) and then builds it making each element but the last a <a>
element linking to the relevant path; last has to be a <span>
element getting the active
class
.
All elements need to be turned to uppercase and separated by a separator, given as the second parameter of the function; the last element can terminate in some common extension like .html
, .htm
, .php
or .asp
; if the name of the last element is index
.something, you treat it as if it wasn't there, sending users automatically to the upper level folder.
A few examples can be more helpful than thousands of words of explanation, so here you have them:
Solution.generate_bc("mysite.com/pictures/holidays.html", " : ").equals( '<a href="/">HOME</a> : <a href="/pictures/">PICTURES</a> : <span class="active">HOLIDAYS</span>' );
Solution.generate_bc("www.codewars.com/users/GiacomoSorbi", " / ").equals( '<a href="/">HOME</a> / <a href="/users/">USERS</a> / <span class="active">GIACOMOSORBI</span>' );
Solution.generate_bc("www.microsoft.com/docs/index.htm", " * ").equals( '<a href="/">HOME</a> * <span class="active">DOCS</span>' );
Seems easy enough?
Well, probably not so much, but we have one last extra rule: if one element (other than the root/home) is longer than 30 characters, you have to shorten it, acronymizing it (i.e.: taking just the initials of every word); url will be always given in the format this-is-an-element-of-the-url
and you should ignore words in this array while acronymizing: ["the","of","in","from","by","with","and", "or", "for", "to", "at", "a"]
; a url composed of more words separated by -
and equal or less than 30 characters long needs to be just uppercased with hyphens replaced by spaces.
Ignore anchors (www.url.com#lameAnchorExample
) and parameters (www.url.com?codewars=rocks&pippi=rocksToo
) when present.
Examples:
Solution.generate_bc("mysite.com/very-long-url-to-make-a-silly-yet-meaningful-example/example.htm", " > ").equals( '<a href="/">HOME</a> > <a href="/very-long-url-to-make-a-silly-yet-meaningful-example/">VLUMSYME</a> > <span class="active">EXAMPLE</span>' );
Solution.generate_bc("www.very-long-site_name-to-make-a-silly-yet-meaningful-example.com/users/giacomo-sorbi", " + ").equals( '<a href="/">HOME</a> + <a href="/users/">USERS</a> + <span class="active">GIACOMO SORBI</span>' );
You will always be provided valid url to webpages in common formats, so you probably shouldn't bother validating them.
If you like to test yourself with actual work/interview related kata, please also consider this one about building a string filter for Angular.js.
Special thanks to the colleague that, seeing my code and commenting that I worked on that as if it was I was on CodeWars, made me realize that it could be indeed a good idea for a kata :)
문제 해석
주어진 URL로부터 BreadCrumb을 생성하는 문제입니다. 사이트 루트는 항상 HOME으로 표시하고요. 마지막 문서는 확장자를 제외해야 합니다. 30자가 넘어가는 메뉴의 경우는 이니셜로 표현하는데요. ["the","of","in","from","by","with","and", "or", "for", "to", "at", "a"]
는 이니셜에서 제외합니다. index 파일을 가지고 있는 주소의 경우 index 문서는 BreadCrumb에 포함이 되지 않습니다. URL에 포함된 파라미터들도 제거하고 수행해야 합니다.
입력값
- String url: BreadCrumb을 추출할 URL
- String separator: BreadCrumb의 각 단계별 구분자
출력값
- String result: 생성된 BreadCrumbs
My Solution
먼저 주소를 자르기 위해서 http://
의 프로토콜을 제거해주고 /
로 split()
했습니다. 그런 다음 0번째는 무조건 HOME이기 때문에 1번째 요소부터 Stream을 생성해서 주소 가공 작업을 해주었습니다. a
태그에 들어갈 주소는 StringBuffer locations
에 담아 주었고요. 전체 결과는 StringBuffer output
에 담았습니다.
String getBcString(String path)
메소드를 하나 정의했습니다. 30자가 넘어가는 주소에 대한 가공 작업을 하기 위한 메소드입니다. 주소가 항상 -
를 구분자로 하고 있기 때문에 30자가 넘어가는 경우 -
로 split()
해주고, 마찬가지로 Stream
을 이용해서 제거 문자열을 필터 해준 후 map
으로 첫 글자만 남겨서 collect
해주었습니다.
import java.util.Arrays;
import java.util.stream.Collectors;
public class Solution {
public static String generate_bc(String url, String separator) {
String[] urlStrings = url.replaceFirst("http[s]?://", "").split("/");
String[] paths = Arrays.stream(urlStrings, 1, urlStrings.length).filter(item -> !item.matches("index[.].*"))
.map(item -> item.substring(0, item.contains("#") ? item.indexOf("#") : item.length()))
.map(item -> item.substring(0, item.contains("?") ? item.indexOf("?") : item.length()))
.map(item -> item.substring(0, item.contains(".") ? item.indexOf(".") : item.length()))
.filter(item -> !"".equals(item)).toArray(String[]::new);
StringBuffer locations = new StringBuffer("/");
StringBuffer output = new StringBuffer();
if (paths.length > 0) {
output.append(String.format("<a href=\"%s\">%s</a>", locations.toString(), "HOME"));
} else {
output.append(String.format("<span class=\"active\">%s</span>", "HOME"));
}
for (int i = 0; i < paths.length - 1; i++) {
locations.append(paths[i]).append("/");
output.append(separator)
.append(String.format("<a href=\"%s\">%s</a>", locations.toString(), getBcString(paths[i])));
}
if (paths.length > 0) {
output.append(separator)
.append(String.format("<span class=\"active\">%s</span>", getBcString(paths[paths.length - 1])));
}
System.out.println(output.toString());
return output.toString();
}
public static String getBcString(String path) {
String bcString = "";
if (path.length() > 30) {
bcString = Arrays.stream(path.split("-"))
.filter(item -> !item.matches("the|of|in|from|by|with|and|or|for|to|at|a"))
.map(item -> item.substring(0, 1).toUpperCase()).collect(Collectors.joining(""));
} else {
bcString = path.toUpperCase().replaceAll("-", " ");
}
return bcString;
}
}
Best Practice
Best Practice와 저의 코드가 작업 순서 자체는 유사한데요. url.replaceAll("https?://|/index[.].*|([.]\\w+)?([?#].*)?|/$", "")
정규식 활용이 대박입니다. 정규식 하나로 파라미터 문자열과 index문서를 모두 제거할 수 있다는 것이 충격이네요.
import java.util.*;
import java.util.stream.*;
public class Solution {
private static Set<String> IGNORE_WORDS = new HashSet<String>(Arrays.asList("the of in from by with and or for to at a".toUpperCase().split(" ")));
public static String generate_bc(String url, String separator) {
List<String> bcLst = new ArrayList();
String[] urlArray = url.replaceAll("https?://|/index[.].*|([.]\\w+)?([?#].*)?|/$", "")
.split("/");
for (int i = 0 ; i < urlArray.length-1 ; i++) {
if (i == 0) bcLst.add("<a href=\"/\">HOME</a>");
else bcLst.add(String.format("<a href=\"/%1$s/\">%2$s</a>", String.join("/", Arrays.copyOfRange(urlArray, 1, i+1)),
formatStr(urlArray[i])));
}
bcLst.add(String.format("<span class=\"active\">%s</span>", urlArray.length == 1 ? "HOME"
: formatStr(urlArray[urlArray.length-1]) ));
return String.join(separator, bcLst.toArray(new String[0]));
}
public static String formatStr(String item) {
item = item.toUpperCase().replace("-"," ");
if (item.length() > 30)
item = Arrays.stream(item.split(" "))
.filter( s -> !IGNORE_WORDS.contains(s))
.map( s -> s.substring(0,1) )
.collect(Collectors.joining(""));
return item;
}
}
'IT Contents > 프로그래밍 팁' 카테고리의 다른 글
Path Finder #2: shortest path, CodeWars Kata 자바 솔루션 (0) | 2021.04.22 |
---|---|
Path Finder #1: can you reach the exit?, CodeWars Kata 자바 솔루션 (0) | 2021.04.21 |
Befunge Interpreter, CodeWars Kata 자바 솔루션 (0) | 2021.04.19 |
Codewars style ranking system, CodeWars Kata 자바 솔루션 (0) | 2021.04.18 |
Sum by Factors, CodeWars Kata 자바 솔루션 (0) | 2021.04.17 |
최근댓글