Map and Set - Lee-hyuna/33-js-concepts-kr GitHub Wiki

์›๋ณธ: http://javascript.info/map-set

์•„๋ž˜์™€ ๊ฐ™์€ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

  • Key ์ง‘ํ•ฉ์„ ์ €์žฅํ•˜๋Š” Object
  • Array ์ง‘ํ•ฉ

์œ„์˜ ๋ฉ”์„œ๋“œ๋กœ๋Š” ์ฝ”๋”ฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ๊ทธ๋ž˜์„œ ์˜ค๋Š˜ ๋ฐฐ์›Œ๋ณผ ์ฃผ์ œ๋Š” Map๊ณผ Set์ด๋‹ค.

Map

Map์€ Object์ฒ˜๋Ÿผ ํ‚ค๋กœ ์—ฐ๊ฒฐ๋œ ๋ฐ์ดํ„ฐ ํ•ญ๋ชฉ์˜ ์ง‘ํ•ฉ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฃผ์š” ์ฐจ์ด์ ์€ Map์ด ๋ชจ๋“  ์ข…๋ฅ˜์˜ ํ‚ค๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋ฉ”์†Œ๋“œ์™€ ํ”„๋กœํผํ‹ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • new Map() โ€“ ๋งต์„ ์ƒ์„ฑํ•œ๋‹ค.
  • map.set(key, value) โ€“ key์™€ value๊ฐ’์„ ์ €์žฅํ•œ๋‹ค.
  • map.get(key) โ€“ key๊ฐ€ map์— ์—†๋Š” ๊ฒฝ์šฐ undefined๋กœ ๊ฐ’์„ ๋ฆฌํ„ดํ•œ๋‹ค.
  • map.has(key) โ€“ key๊ฐ€ ์žˆ์œผ๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด false๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
  • map.delete(key) โ€“ key๊ฐ’์„ ์‚ญ์ œํ•œ๋‹ค.
  • map.clear() โ€“ map์„ ๋Œ๋ฉฐ ๋ชจ๋‘ ์‚ญ์ œํ•œ๋‹ค.
  • map.size โ€“ ์ตœ๊ทผ ์‚ฌ์šฉ๋œ ์—˜๋ฆฌ๋จผํŠธ์˜ ๊ฐฏ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด:

let map = new Map();

map.set('1', 'str1');   // a string key
map.set(1, 'num1');     // a numeric key
map.set(true, 'bool1'); // a boolean key

// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

๋ณด์‹œ๋‹ค์‹œํ”ผ, Object์™€ ๋‹ฌ๋ฆฌ key๋Š” ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š๋Š”๋‹ค. ๋ชจ๋“  ์œ ํ˜•์˜ key๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

map์€ Object๋ฅผ key๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด:

let john = { name: "John" };

// for every user, let's store their visits count
let visitsCountMap = new Map();

// john is the key for the map
visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123

Object๋ฅผ key๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ฐ€์žฅ ๋ˆˆ์— ๋„๊ณ  ์ค‘์š”ํ•œ map ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ด๋‹ค. ๋ฌธ์ž์—ด ํ‚ค์˜ ๊ฒฝ์šฐ ๊ฐœ์ฒด๋Š” ์ •์ƒ์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ๊ฐœ์ฒด ํ‚ค๋Š” ์ •์ƒ์ผ ์ˆ˜ ์—†๋‹ค.

let john = { name: "John" };

let visitsCountObj = {}; // try to use an object

visitsCountObj[john] = 123; // try to use john object as the key

// That's what got written!
alert( visitsCountObj["[object Object]"] ); // 123

visitsCountObj๋Š” ๊ฐ์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์— john๊ณผ ๊ฐ™์€ ๋ชจ๋“  key๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋ฏ€๋กœ ๋ฌธ์ž์—ด ํ‚ค "[object Object]"๋ผ๊ณ  ๋…ธ์ถœํ•˜๋Š”๋ฐ ์ด alert๊ฐ’์„ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹ˆ๋‹ค.

๋งต์ด ํ‚ค๋ฅผ ๋น„๊ตํ•˜๋Š” ๋ฐฉ๋ฒ•

๋™๋“ฑ์„ฑ์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ๋งต์€ SameValueZero ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•œ๋‹ค. ์—„๊ฒฉํ•œ ํ‰๋“ฑ === ๊ณผ ๋™์ผํ•˜์ง€๋งŒ, ์ฐจ์ด์ ์€ NaN์ด NaN๊ณผ ๋™๋“ฑํ•˜๋‹ค๊ณ  ์—ฌ๊ฒจ์ง„๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ NaN๋„ key๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž ์ •์˜ํ•  ์ˆ˜ ์—†๋‹ค.

Chaining

๋ชจ๋“  map.set์€ map ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ, ๋‹ค์Œ ํ˜ธ์ถœ์„ "์ฒด์ธ"ํ•  ์ˆ˜ ์žˆ๋‹ค.

map.set('1', 'str1')
  .set(1, 'num1')
  .set(true, 'bool1');

Iteration over Map

map์—์„œ ๋ฃจํ”„๋ฅผ ๋„๋Š” ๋ฐฉ๋ฒ•์€ 3๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  • map.keys() โ€“ key๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
  • map.values() โ€“ value๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
  • map.entries() โ€“ [key, value] ํ•ญ๋ชฉ์— ๋Œ€ํ•ด ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ’์„ ๋ฆฌํ„ดํ•œ๋‹ค. ์ด ๊ฐ’์€ ๊ธฐ๋ณธ์ ์œผ๋กœ for..of์—์„œ ์‚ฌ์šฉ๋œ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด:



let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

// iterate over keys (vegetables)
for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomatoes, onion
}

// iterate over values (amounts)
for (let amount of recipeMap.values()) {
  alert(amount); // 500, 350, 50
}

// iterate over [key, value] entries
for (let entry of recipeMap) { // the same as of recipeMap.entries()
  alert(entry); // cucumber,500 (and so on)
}

๋ฐ˜๋ณต์€ ๊ฐ’์ด ์‚ฝ์ž…๋œ ์ˆœ์„œ๋Œ€๋กœ ์ง„ํ–‰๋œ๋‹ค. map์€ ์ผ๋ฐ˜ Object์™€ ๋‹ฌ๋ฆฌ ์ด ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.

// runs the function for each (key, value) pair
recipeMap.forEach( (value, key, map) => {
  alert(`${key}: ${value}`); // cucumber: 500 etc
});

Object.entries: Map from Object

map์ด ์ƒ์„ฑ๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ดˆ๊ธฐํ™”๋ฅผ ์œ„ํ•ด ํ‚ค/๊ฐ’ ์Œ์œผ๋กœ ๋ฐฐ์—ด(๋˜๋Š” ๋‹ค๋ฅธ ํ—ˆ์šฉ ๊ฐ€๋Šฅ)์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

// array of [key, value] pairs
let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);

alert( map.get('1') ); // str1

์ผ๋ฐ˜ ๊ฐœ์ฒด๊ฐ€ ์žˆ๊ณ  ์—ฌ๊ธฐ์„œ map์„ ์ž‘์„ฑํ•˜๋ ค๋ฉด ๊ฐœ์ฒด์˜ ํ‚ค/๊ฐ’ ์Œ ๋ฐฐ์—ด์„ ํ•ด๋‹น ํ˜•์‹์œผ๋กœ ์ •ํ™•ํ•˜๊ฒŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ธฐ๋ณธ ์ œ๊ณต ๋ฐฉ๋ฒ• Object.entries(obj)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด์™€ ๊ฐ™์€ ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ์ง€๋„๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));

alert( map.get('name') ); // John

์—ฌ๊ธฐ์„œ Object.entries๋Š” ["name","John", ["age", 30] ๋“ฑ์˜ ํ‚ค/๊ฐ’ ์Œ์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Object.fromEntries: Object from Map

Object.entries(obj)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๋ฐ˜ ๊ฐœ์ฒด์—์„œ map์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฉ๊ธˆ ์‚ดํŽด๋ณด์•˜๋‹ค.

Object.fromEntries๋Š” [key, value] ์Œ์˜ ๋ฐฐ์—ด์„ ์ง€์ •ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

let prices = Object.fromEntries([
  ['banana', 1],
  ['orange', 2],
  ['meat', 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

alert(prices.orange); // 2

Object.fromEntries๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ map์—์„œ ์ผ๋ฐ˜ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์šฐ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ง€๋„์— ์ €์žฅํ•˜์ง€๋งŒ, ์ผ๋ฐ˜ ๊ฐ์ฒด๋ฅผ ์˜ˆ์ƒํ•˜๋Š” ์ œ3์ž ์ฝ”๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด:

let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);

let obj = Object.fromEntries(map.entries()); // make a plain object (*)

// done!
// obj = { banana: 1, orange: 2, meat: 4 }

alert(obj.orange); // 2

call to map.entries()๋Š” Object.fromEntries์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ํ˜•์‹์œผ๋กœ ํ‚ค/๊ฐ’ ์Œ์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

line(*)์„ ๋” ์งง๊ฒŒ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค.

let obj = Object.fromEntries(map); // omit .entries()

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Object.fromEntries๋„ ์ธ์ˆ˜๋กœ ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋ฅผ ์˜ˆ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋ฐ˜๋“œ์‹œ ๋ฐฐ์—ด์€ ์•„๋‹ˆ๋‹ค.

๋˜ํ•œ map์˜ ํ‘œ์ค€ ๋ฐ˜๋ณต์€ map.entries()์™€ ๋™์ผํ•œ ํ‚ค/๊ฐ’ ์Œ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์ง€๋„์™€ ๊ฐ™์€ ํ‚ค/๊ฐ’์„ ๊ฐ€์ง„ ํ‰๋ฒ”ํ•œ ๋ฌผ์ฒด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

Set

set์€ ๊ฐ ๊ฐ’์ด ํ•œ ๋ฒˆ๋งŒ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ˆ˜ ์œ ํ˜• ์ปฌ๋ ‰์ด๋‹ค. ์ฐธ๊ณ ๋กœ key๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

  • new set(iterable) โ€“ ์„ธํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐœ์ฒด(์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐฐ์—ด)๊ฐ€ ์ œ๊ณต๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ฐœ์ฒด์—์„œ ์„ธํŠธ๋กœ ๊ฐ’์„ ๋ณต์‚ฌํ•œ๋‹ค.
  • set.add(value) โ€“ ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๊ณ  set ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • set.delete(value) โ€“ ๊ฐ’์„ ์‚ญ์ œํ•˜๊ณ , ํ˜ธ์ถœ ์ˆœ๊ฐ„์— ๊ฐ’์ด ์กด์žฌํ•˜๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • set.has(value) โ€“ ๊ฐ’์ด set์— ์žˆ์œผ๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • set.clear() โ€“ ์ง‘ํ•ฉ์—์„œ ๋ชจ๋“  ๊ฒƒ์„ ์ œ๊ฑฐํ•œ๋‹ค.
  • set.size โ€“ ์š”์†Œ ๊ฐœ์ˆ˜.

๊ธฐ๋ณธ ๊ธฐ๋Šฅ์€ ๋™์ผํ•œ ๊ฐ’์„ ๊ฐ€์ง„ set.add(value)์˜ ๋ฐ˜๋ณต ํ˜ธ์ถœ์€ ์•„๋ฌด ๊ฒƒ๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ฐ ๊ฐ’์ด set์— ํ•œ ๋ฒˆ๋งŒ ๋‚˜ํƒ€๋‚˜๋Š” ์ด์œ ์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐฉ๋ฌธ๊ฐ๋“ค์ด ์˜ค๊ณ , ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์„ ๊ธฐ์–ตํ•˜๊ธฐ๋ฅผ ์›ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ๋ฐ˜๋ณต์ ์ธ ๋ฐฉ๋ฌธ์ด ์ค‘๋ณต์œผ๋กœ ์ด์–ด์ง€๋ฉด ์•ˆ ๋œ๋‹ค. ๋ฐฉ๋ฌธ์ž๋Š” ํ•œ ๋ฒˆ๋งŒ "count"ํ•ด์•ผ ํ•œ๋‹ค.

let set = new Set();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

// visits, some users come multiple times
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// set keeps only unique values
alert( set.size ); // 3

for (let user of set) {
  alert(user.name); // John (then Pete and Mary)
}

์ง‘ํ•ฉ์˜ ๋Œ€์•ˆ์€ ์‚ฌ์šฉ์ž ๋ฐฐ์—ด๊ณผ arr.find๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฝ์ž…ํ•  ๋•Œ๋งˆ๋‹ค ์ค‘๋ณต ํ•ญ๋ชฉ์„ ํ™•์ธํ•˜๋Š” ์ฝ”๋“œ์ผ ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ์„ฑ๋Šฅ์€ ํ›จ์”ฌ ๋” ๋‚˜์  ๊ฒƒ์ด๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์ „์ฒด array๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์š”์†Œ๋ฅผ ๊ฒ€์‚ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์„ค์ •์€ ๊ณ ์œ ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ ํ›จ์”ฌ ๋” ์ž˜ ์ตœ์ ํ™” ๋˜์–ด์žˆ๋‹ค.

Iteration over Set

๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” for..of ํ˜น์€ forEach๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) alert(value);

// the same with forEach:
set.forEach((value, valueAgain, set) => {
  alert(value);
});

forEach์— ์ „๋‹ฌ๋œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—๋Š” ๊ฐ’, ๊ทธ ๋‹ค์Œ ๋™์ผํ•œ valueAgain, ๊ทธ๋ฆฌ๊ณ  ๋Œ€์ƒ ๊ฐœ์ฒด์˜ ์„ธ ๊ฐ€์ง€ ์ธ์ˆ˜๊ฐ€ ์žˆ๋‹ค. ์‹ค์ œ๋กœ ๋™์ผํ•œ ๊ฐ’์ด ์ธ์ˆ˜์— ๋‘ ๋ฒˆ ๋‚˜ํƒ€๋‚œ๋‹ค.

์ด๋Š” callback์ด ์ „๋‹ฌ๋œ Map๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ๊ฐ์—๋Š” ์„ธ ๊ฐœ์˜ ์ธ์ˆ˜๊ฐ€ ์žˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” map์„ ์‰ฝ๊ฒŒ Set๋กœ ๊ต์ฒดํ•˜๊ฑฐ๋‚˜ ๊ทธ ๋ฐ˜๋Œ€๋กœ ๊ต์ฒดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

map์ด ๋ฐ˜๋ณต๊ธฐ์— ๋Œ€ํ•ด ๊ฐ€์ง€๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ฐฉ๋ฒ•๋„ ์ง€์›๋œ๋‹ค.

  • set.keys() โ€“ ๊ฐ’์— ๋Œ€ํ•ด ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐœ์ฒด๋ฅผ ๋ฐ˜ํ™˜
  • set.values() โ€“ set.keys()์™€ ๋™์ผํ•˜๋ฉฐ, ์ง€๋„์™€์˜ ํ˜ธํ™˜์„ฑ
  • set.entries() โ€“ [value, value] ํ•ญ๋ชฉ์— ๋Œ€ํ•ด ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐœ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ map๊ณผ ํ˜ธํ™˜