hangul-search
Advanced tools
Comparing version 1.2.0 to 1.3.0
{ | ||
"name": "hangul-search", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Json객체에서 한글 검색을 수행. 한글 초성 검색을 지원. Korean search within JSON objects. Supports Korean initial consonant search.", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
119
README.md
@@ -11,10 +11,11 @@ ## hansearch | ||
* 매우 심플하고 빠릅니다. | ||
* 영문 대소문자 구분없이 검색을 지원합니다. | ||
* 한글 초성 검색을 지원합니다. | ||
* 기본적으로 exact matching 으로 동작합니다. | ||
* 특수문자에 대한 검색을 지원합니다. (Contributions by [hwahyeon](https://github.com/hwahyeonhttps:/)) | ||
* Typescript 를 지원하도록 구현하였습니다. | ||
* AMD, CJS 스펙을 모두 지원할 수 있도록 UMD 패턴으로 작성하였습니다. | ||
* 사용법이 매우 간편합니다. | ||
- 매우 심플하고 빠릅니다. | ||
- 영문 대소문자 구분없이 검색을 지원합니다. | ||
- 한글 초성 검색을 지원합니다. | ||
- 기본적으로 exact matching 으로 동작합니다. | ||
- 특수문자에 대한 검색을 지원합니다. (Contributions by [hwahyeon](https://github.com/hwahyeonhttps:/)) | ||
- 마지막 입력 문자를 분해하여 다음 문자열 검색에 사용합니다. 예) "많"을 입력한 경우 "만화" 도 함께 검색됩니다. | ||
- Typescript 를 지원하도록 구현하였습니다. | ||
- AMD, CJS 스펙을 모두 지원할 수 있도록 UMD 패턴으로 작성하였습니다. | ||
- 사용법이 매우 간편합니다. | ||
@@ -24,37 +25,40 @@ [라이브 데모 확인하기](https://hwantage.github.io/hansearch/demo/) | ||
## Usage 1 | ||
노드 패키지를 설치해서 테스트 가능합니다. 간편하게 테스트 해보시려면 `Usage 2` 섹션을 참고하세요. | ||
npm 패키지로 설치합니다. | ||
```shell | ||
npm i hangul-search | ||
``` | ||
코드를 작성합니다. | ||
```js | ||
// 라이브러리 import | ||
import hansearch from "hangul-search"; | ||
// 라이브러리 import | ||
import hansearch from "hangul-search"; | ||
// Json 객체 정의 | ||
var json = | ||
[ | ||
{ | ||
"title": "Javascript find, findIndex, filter", | ||
"users": ["김정환", "홍길동", "강감찬", "아이유"], | ||
"description": "구글 네이버 다음" | ||
}, | ||
{ | ||
"title": "Javascript 직렬화(serialization)", | ||
"users": ["골리앗", "이순신", "김길동"], | ||
"description": "직렬화를 알아본다." | ||
}, | ||
{ | ||
"title": "Javascript 템플릿 리터럴(Template literal)", | ||
"users": ["James", "Tom", "David"], | ||
"description": "고글 가글 고고씽" | ||
} | ||
] | ||
// Json 객체 정의 | ||
var json = [ | ||
{ | ||
title: "Javascript find, findIndex, filter", | ||
users: ["김정환", "홍길동", "강감찬", "아이유"], | ||
description: "구글 네이버 다음", | ||
}, | ||
{ | ||
title: "Javascript 직렬화(serialization)", | ||
users: ["골리앗", "이순신", "김길동"], | ||
description: "직렬화를 알아본다.", | ||
}, | ||
{ | ||
title: "Javascript 템플릿 리터럴(Template literal)", | ||
users: ["James", "Tom", "David"], | ||
description: "고글 가글 고고씽", | ||
}, | ||
]; | ||
// 검색 수행 | ||
const result = hansearch(json, "ㅈ렬화"); | ||
console.log(result); | ||
// 검색 수행 | ||
const result = hansearch(json, "ㅈ렬화"); | ||
console.log(result); | ||
/* 출력 결과 | ||
/* 출력 결과 | ||
{ | ||
@@ -74,25 +78,26 @@ "items" : | ||
## Usage 2 | ||
CDN을 이용하여 실행가능합니다. | ||
```html | ||
<script src="https://hwantage.github.io/hansearch/src/index.js" type="text/javascript"></script> | ||
<script type="text/javascript"> | ||
var json = | ||
[ | ||
{ | ||
"title": "Javascript find, findIndex, filter", | ||
"users": ["김정환", "홍길동", "강감찬", "아이유"], | ||
"description": "구글 네이버 다음" | ||
}, | ||
{ | ||
"title": "Javascript 직렬화(serialization)", | ||
"users": ["골리앗", "이순신", "김길동"], | ||
"description": "직렬화를 알아본다." | ||
}, | ||
{ | ||
"title": "Javascript 템플릿 리터럴(Template literal)", | ||
"users": ["James", "Tom", "David"], | ||
"description": "고글 가글 고고씽" | ||
} | ||
var json = [ | ||
{ | ||
title: "Javascript find, findIndex, filter", | ||
users: ["김정환", "홍길동", "강감찬", "아이유"], | ||
description: "구글 네이버 다음", | ||
}, | ||
{ | ||
title: "Javascript 직렬화(serialization)", | ||
users: ["골리앗", "이순신", "김길동"], | ||
description: "직렬화를 알아본다.", | ||
}, | ||
{ | ||
title: "Javascript 템플릿 리터럴(Template literal)", | ||
users: ["James", "Tom", "David"], | ||
description: "고글 가글 고고씽", | ||
}, | ||
]; | ||
var result = hansearch(json, "ㅈ렬화"); | ||
@@ -121,14 +126,14 @@ console.log(result); | ||
```js | ||
var result = hansearch(json, "ㅈ렬화"); // 모든 키를 대상으로 검색을 수행합니다. | ||
var result = hansearch(json, "ㅈ렬화", ["title"]); // title 키를 대상으로 검색을 수행합니다. | ||
var result = hansearch(json, "ㅈ렬화", ["title", "users"]); // title과 users 키를 대상으로 검색을 수행합니다. | ||
var result = hansearch(json, "ㅈ렬화"); // 모든 키를 대상으로 검색을 수행합니다. | ||
var result = hansearch(json, "ㅈ렬화", ["title"]); // title 키를 대상으로 검색을 수행합니다. | ||
var result = hansearch(json, "ㅈ렬화", ["title", "users"]); // title과 users 키를 대상으로 검색을 수행합니다. | ||
``` | ||
.mark() 메소드 체이닝을 지원합니다. 일치한 검색어를 `<mark>` 태그로 감싸주며, 원하는 태그를 인자로 넘겨줄 수 있습니다. | ||
```js | ||
var result = hansearch(json, "ㅈ렬화").mark(); // 검색어와 일치한 단어를 <mark> 태그로 감싼 결과를 리턴합니다. | ||
var result = hansearch(json, "ㅈ렬화").mark("tags"); // 검색어와 일치한 단어를 <tags> 태그로 감싼 결과를 리턴합니다. | ||
var result = hansearch(json, "ㅈ렬화").mark(); // 검색어와 일치한 단어를 <mark> 태그로 감싼 결과를 리턴합니다. | ||
var result = hansearch(json, "ㅈ렬화").mark("tags"); // 검색어와 일치한 단어를 <tags> 태그로 감싼 결과를 리턴합니다. | ||
``` | ||
## References | ||
@@ -135,0 +140,0 @@ |
@@ -18,2 +18,6 @@ /*! | ||
const CHO_HANGUL = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"]; | ||
const JUNG_HANGUL = ["ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ", "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ"]; | ||
const JONG_HANGUL = ["", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"]; | ||
const MAP_DOUBLE_JONG = { ㄳ: "ㄱㅅ", ㄵ: "ㄴㅈ", ㄶ: "ㄴㅎ", ㄺ: "ㄹㄱ", ㄻ: "ㄹㅁ", ㄼ: "ㄹㅂ", ㄽ: "ㄹㅅ", ㄾ: "ㄹㅌ", ㄿ: "ㄹㅍ", ㅀ: "ㄹㅎ", ㅄ: "ㅂㅅ" }; | ||
const HAN_START_CHAR = "가".charCodeAt(); | ||
@@ -24,3 +28,3 @@ const CHO_PERIOD = Math.floor("까".charCodeAt() - "가".charCodeAt()); | ||
// 초성, 중성, 종성 값을 사용하여 각각의 크기를 고려하여 한글 문자의 코드 포인트를 계산 | ||
const combineHangul = function (cho, jung, jong) { | ||
const combineHangul = (cho, jung, jong) => { | ||
return String.fromCharCode(HAN_START_CHAR + cho * CHO_PERIOD + jung * JUNG_PERIOD + jong); | ||
@@ -30,3 +34,3 @@ }; | ||
// 초성검색 | ||
const makeRegexByCho = function (keyWord = "") { | ||
const makeRegexByCho = (keyWord = "") => { | ||
const escapedSearch = keyWord.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); | ||
@@ -37,8 +41,52 @@ const regex = CHO_HANGUL.reduce((acc, cho, index) => acc.replace(new RegExp(cho, "g"), `[${combineHangul(index, 0, 0)}-${combineHangul(index + 1, 0, -1)}]`), escapedSearch); | ||
const hansearch = function (jsonObj, keyWord, keys = []) { | ||
const regex = makeRegexByCho(keyWord); | ||
// 초성, 중성, 종성 값으로 한글 재조합 | ||
const johapHangul = (cho, jung, jong) => { | ||
return String.fromCharCode(HAN_START_CHAR + CHO_HANGUL.indexOf(cho) * CHO_PERIOD + JUNG_HANGUL.indexOf(jung) * JUNG_PERIOD + (jong ? JONG_HANGUL.indexOf(jong) : 0)); | ||
}; | ||
// 사용자의 예상 기대 결과 도출을 위해 검색 키워드를 생성. 마지막 문자를 분해 조합. | ||
const makeSearchWords = (keyWord = "") => { | ||
let keywords = [keyWord]; | ||
const preCharacters = keyWord.slice(0, keyWord.length - 1); // 마지막 문자를 제외한 모든 문자열 | ||
const lastCharacter = keyWord.charAt(keyWord.length - 1); // 마지막 문자 | ||
if (lastCharacter >= "가" && lastCharacter <= "힣") { | ||
// 모음이 포함된 경우 | ||
const char_code = lastCharacter.charCodeAt() - HAN_START_CHAR; | ||
const cho = Math.floor(char_code / CHO_PERIOD); | ||
const jung = Math.floor((char_code % CHO_PERIOD) / JUNG_PERIOD); | ||
const jong = char_code % JUNG_PERIOD; | ||
const cho_char = CHO_HANGUL[cho]; | ||
const jung_char = JUNG_HANGUL[jung]; | ||
const jong_char = JONG_HANGUL[jong]; | ||
if (jong > 0) { | ||
if (MAP_DOUBLE_JONG.hasOwnProperty(jong_char)) { | ||
// 겹받침인 경우 | ||
let result = johapHangul(cho_char, jung_char, MAP_DOUBLE_JONG[jong_char][0]); | ||
keywords.push(preCharacters + result); | ||
keywords.push(preCharacters + result + MAP_DOUBLE_JONG[jong_char][1]); | ||
} else { | ||
// 홑받침인 경우 | ||
keywords.push(preCharacters + johapHangul(cho_char, jung_char)); | ||
keywords.push(preCharacters + johapHangul(cho_char, jung_char) + jong_char); | ||
} | ||
} | ||
} else { | ||
// 초성만 있는 경우 | ||
if (MAP_DOUBLE_JONG.hasOwnProperty(lastCharacter)) { | ||
keywords.push(preCharacters + MAP_DOUBLE_JONG[lastCharacter]); | ||
} | ||
} | ||
return keywords; | ||
}; | ||
const hansearch = (jsonObj, keyWord, keys = []) => { | ||
const searchWords = makeSearchWords(keyWord); | ||
const regexArray = searchWords.map((word) => makeRegexByCho(word)).filter((regex) => regex); | ||
//console.log(searchWords, regexArray); | ||
let searchResult = jsonObj.filter((obj) => { | ||
for (const key in obj) { | ||
if (obj.hasOwnProperty(key) && (keys.length === 0 || keys.includes(key))) { | ||
if (regex.test(obj[key])) { | ||
if (regexArray.some((regex) => regex.test(obj[key]))) { | ||
return true; | ||
@@ -53,3 +101,3 @@ } | ||
items: searchResult, | ||
mark: function (tag = "mark") { | ||
mark: (tag = "mark") => { | ||
return { | ||
@@ -63,5 +111,7 @@ items: searchResult.map((obj) => { | ||
// 배열인 경우 각 원소에 대해 처리 | ||
markedObj[key] = obj[key].map((item) => item.replace(regex, `<${tag}>$&</${tag}>`)); | ||
markedObj[key] = obj[key].map((item) => { | ||
return regexArray.reduceRight((acc, regex) => acc.replace(regex, `<${tag}>$&</${tag}>`), item); | ||
}); | ||
} else { | ||
markedObj[key] = obj[key].replace(regex, `<${tag}>$&</${tag}>`); | ||
markedObj[key] = regexArray.reduceRight((acc, regex) => acc.replace(regex, `<${tag}>$&</${tag}>`), obj[key]); | ||
} | ||
@@ -68,0 +118,0 @@ } else markedObj[key] = obj[key]; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16652
135
142