JavaScript 세트의 객체 동일성을 사용자 정의하는 방법
New ES 6(Harmony)에서는 새로운 Set 객체가 도입됩니다.Set에서 사용되는 ID 알고리즘은 다음과 같습니다.===
연산자이므로 객체를 비교하기에 적합하지 않습니다.
var set = new Set();
set.add({a:1});
set.add({a:1});
console.log([...set.values()]); // Array [ Object, Object ]
개체를 상세하게 비교하기 위해 개체 집합에 대한 동일성을 사용자 지정하는 방법은 무엇입니까?와 같은 요?equals(Object)
업데이트 2022년 3월
현재 Javascript에 Records와 Tuples(기본적으로 불변의 Objects와 Arrays)를 추가하는 제안이 있습니다.이 제안서에서는 다음 방법을 사용하여 기록과 튜플을 직접 비교합니다.===
★★★★★★★★★★★★★★★★★」!==
서 오브젝트 이 값도 합니다.Set
★★★★★★★★★★★★★★★★★」Map
오브젝트는 레코드 또는 튜플의 값을 키 비교/검색에 사용하여 여기서 요구하는 내용을 해결합니다.
레코드 및 튜플은 불변(수정할 수 없음)이며 (개체 참조뿐만 아니라 내용에 따라) 값에 따라 쉽게 비교되기 때문에 맵 및 세트에서는 객체 콘텐츠를 키로 사용할 수 있으며 제안된 사양에서는 세트 및 맵에 대해 이 기능을 명시적으로 명명합니다.
이 질문에서는 상세 객체 비교를 지원하기 위해 Set 비교의 커스터마이즈 가능성을 요청했습니다.이것은 세트 비교의 커스터마이즈 가능성을 제안하지는 않지만 오브젝트나 어레이 대신 새로운 레코드나 태플을 사용하면 여기서 원래의 문제를 해결할 수 있는 상세한 오브젝트 비교를 직접 지원합니다.
주의: 이 제안은 2020년 중반에 2단계로 진행되었습니다.그것은 최근에 진전되고 있지만, 확실히 끝나지 않았다.
이 새로운 제안에 대한 Mozilla 작업은 여기에서 추적할 수 있습니다.
원답
ES6Set
개체에 비교 방법 또는 사용자 지정 비교 확장성이 없습니다.
.has()
,.add()
★★★★★★★★★★★★★★★★★」.delete()
메서드는 동일한 실제 객체 또는 원시 값의 경우에만 작동하며, 해당 논리에만 연결하거나 대체할 수 있는 수단이 없습니다.
는 '자신의 목적어'에서될 수 .Set
를 치환합니다..has()
,.add()
★★★★★★★★★★★★★★★★★」.delete()
deep comparison deep object comparison first to first the이 이미 이 되는 항목이 때문에 않을 수 .Set
기존의 의 커스텀 한 후 원래 bluet force를 .add()
.
5.2 맵과 세트가 키와 값을 비교하는 방법을 구성할 수 없는 이유는 무엇입니까?
질문:.어떤 맵 키와 어떤 set 요소가 동등하다고 생각되는지를 설정하는 방법이 있으면 좋겠습니다.왜 없어?
답변: 이 기능은 적절하고 효율적으로 구현하기 어렵기 때문에 연기되었습니다.1가지 옵션은 동등성을 지정하는 컬렉션에 콜백을 핸드백하는 것입니다.
Java에서 사용할 수 있는 다른 옵션은 객체가 구현하는 메서드(Java에서는 equals())를 통해 동등성을 지정하는 것입니다.단, 이 접근방식은 가변 객체에 문제가 있습니다.일반적으로 객체가 변경되면 컬렉션 내의 "위치"도 변경해야 합니다.하지만 Java에서는 그렇지 않습니다.JavaScript는 아마도 특별한 불변 객체(이른바 가치 객체)에 대한 값별 비교만 가능하게 하는 안전한 경로를 택할 것입니다.값에 의한 비교는 두 값이 동일한 경우 동일한 것으로 간주됨을 의미합니다.기본 값은 JavaScript에서 값으로 비교됩니다.
jfriend00의 답변에서 언급한 바와 같이 평등 관계의 커스터마이즈는 가능하지 않을 것입니다.
다음 코드는 계산 효율이 높은(메모리 비용이 많이 드는) 회피책의 개요를 나타냅니다.
class GeneralSet {
constructor() {
this.map = new Map();
this[Symbol.iterator] = this.values;
}
add(item) {
this.map.set(item.toIdString(), item);
}
values() {
return this.map.values();
}
delete(item) {
return this.map.delete(item.toIdString());
}
// ...
}
된 각 는 반드시 .toIdString()
이치노 사물은 두 가지 사물이 동일한 됩니다.toIdString
방법
상위 답변에서 언급했듯이, 가변 객체에 대해 동등성을 커스터마이즈하는 것은 문제가 있습니다.좋은 소식은 (아직 아무도 이것에 대해 언급하지 않은 것이 놀랍습니다)불변의 JS라고 불리는 매우 인기 있는 라이브러리가 있다는 것입니다.이 라이브러리는 당신이 찾고 있는 깊은 가치 균등 시멘틱스를 제공하는 풍부한 불변의 타입을 제공합니다.
다음은 불변-js를 사용한 예입니다.
const { Map, Set } = require('immutable');
var set = new Set();
set = set.add(Map({a:1}));
set = set.add(Map({a:1}));
console.log([...set.values()]); // [Map {"a" => 1}]
여기에 답변을 추가하기 위해 커스텀 해시함수, 커스텀 등호함수를 취하여 버킷에 등가(커스텀) 해시가 있는 개별 값을 저장하는 Map 래퍼를 구현했습니다.
예상대로 이것은 czerny의 문자열 연결 방식보다 느린 것으로 나타났습니다.
출처 : https://github.com/makoConstruct/ValueMap
아마 당신은 그것을 사용해 볼 수 있을 것이다.JSON.stringify()
깊이 있는 객체 비교를 수행합니다.
예를 들어 다음과 같습니다.
const arr = [
{name:'a', value:10},
{name:'a', value:20},
{name:'a', value:20},
{name:'b', value:30},
{name:'b', value:40},
{name:'b', value:40}
];
const names = new Set();
const result = arr.filter(item => !names.has(JSON.stringify(item)) ? names.add(JSON.stringify(item)) : false);
console.log(result);
직접 비교하는 것은 불가능할 것 같지만 키가 정렬되어 있으면 JSON.stringify가 작동합니다.제가 댓글에서 지적했듯이
JSON.stringify({a:1, b:2})!== JSON.stringify({b:2, a:1});
단, 커스텀 스트링라이즈 방식으로 해결할 수 있습니다.먼저 방법을 씁니다.
커스텀 Stringify
Object.prototype.stringifySorted = function(){
let oldObj = this;
let obj = (oldObj.length || oldObj.length === 0) ? [] : {};
for (let key of Object.keys(this).sort((a, b) => a.localeCompare(b))) {
let type = typeof (oldObj[key])
if (type === 'object') {
obj[key] = oldObj[key].stringifySorted();
} else {
obj[key] = oldObj[key];
}
}
return JSON.stringify(obj);
}
세트
이제 세트를 사용합니다.그러나 객체 대신 문자열 집합을 사용합니다.
let set = new Set()
set.add({a:1, b:2}.stringifySorted());
set.has({b:2, a:1}.stringifySorted());
// returns true
모든 값 가져오기
세트를 만들고 값을 추가한 후 다음 방법으로 모든 값을 얻을 수 있습니다.
let iterator = set.values();
let done = false;
while (!done) {
let val = iterator.next();
if (!done) {
console.log(val.value);
}
done = val.done;
}
다음 링크에서는 모든 것을 1개의 파일로 정리합니다.http://tpcg.io/FnJg2i
Typscript 사용자의 경우 다른 사용자(특히 czerny)의 답변은 적절한 타입 세이프 및 재사용 가능한 베이스 클래스로 일반화할 수 있습니다.
/**
* Map that stringifies the key objects in order to leverage
* the javascript native Map and preserve key uniqueness.
*/
abstract class StringifyingMap<K, V> {
private map = new Map<string, V>();
private keyMap = new Map<string, K>();
has(key: K): boolean {
let keyString = this.stringifyKey(key);
return this.map.has(keyString);
}
get(key: K): V {
let keyString = this.stringifyKey(key);
return this.map.get(keyString);
}
set(key: K, value: V): StringifyingMap<K, V> {
let keyString = this.stringifyKey(key);
this.map.set(keyString, value);
this.keyMap.set(keyString, key);
return this;
}
/**
* Puts new key/value if key is absent.
* @param key key
* @param defaultValue default value factory
*/
putIfAbsent(key: K, defaultValue: () => V): boolean {
if (!this.has(key)) {
let value = defaultValue();
this.set(key, value);
return true;
}
return false;
}
keys(): IterableIterator<K> {
return this.keyMap.values();
}
keyList(): K[] {
return [...this.keys()];
}
delete(key: K): boolean {
let keyString = this.stringifyKey(key);
let flag = this.map.delete(keyString);
this.keyMap.delete(keyString);
return flag;
}
clear(): void {
this.map.clear();
this.keyMap.clear();
}
size(): number {
return this.map.size;
}
/**
* Turns the `key` object to a primitive `string` for the underlying `Map`
* @param key key to be stringified
*/
protected abstract stringifyKey(key: K): string;
}
구현의 예는 다음과 같습니다.그냥 이 명령어를 덮어쓰기만 하면 됩니다.stringifyKey
방법.제 경우엔 몇 개를 끈으로 묶어서uri
소유물.
class MyMap extends StringifyingMap<MyKey, MyValue> {
protected stringifyKey(key: MyKey): string {
return key.uri.toString();
}
}
예를 들어, 이것이 일반적인 사용 예라고 할 수 있습니다.Map<K, V>
.
const key1 = new MyKey(1);
const value1 = new MyValue(1);
const value2 = new MyValue(2);
const myMap = new MyMap();
myMap.set(key1, value1);
myMap.set(key1, value2); // native Map would put another key/value pair
myMap.size(); // returns 1, not 2
다른 남자들 말처럼 그것을 할 수 있는 토종 방법은 아직 없다.다만, 커스텀 컴퍼레이터를 사용해 어레이를 구별하는 경우는,reduce
방법.
function distinct(array, equal) {
// No need to convert it to a Set object since it may give you a wrong signal that the set can work with your objects.
return array.reduce((p, c) => {
p.findIndex((element) => equal(element, c)) > -1 || p.push(c);
return p;
}, []);
}
// You can call this method like below,
const users = distinct(
[
{id: 1, name: "kevin"},
{id: 2, name: "sean"},
{id: 1, name: "jerry"}
],
(a, b) => a.id === b.id
);
...
Set/Map 키와 같은 TypeArray의 특수한 케이스이지만 빈번한 케이스에 적합한 문자열화 방법:
const key = String.fromCharCode(...new Uint16Array(myArray.buffer));
쉽게 변환할 수 있는 가장 짧은 고유 문자열을 생성합니다.단, 이 문자열이 Low 및 High Resolates에 관한 표시에 항상 유효한 것은 아닙니다.Set and Map이 대리 유효성을 무시하는 것 같습니다.Firefox와 Chrome에서 측정한 바와 같이 확산 연산자는 동작이 느립니다.myArray의 크기가 고정되어 있는 경우 다음을 쓸 때 더 빨리 실행됩니다.
const a = new Uint16Array(myArray.buffer); // here: myArray = Uint32Array(2) = 8 bytes
const key = String.fromCharCode(a[0],a[1],a[2],a[3]); // 8 bytes too
이 키 구축 방법의 가장 중요한 장점은 다음과 같습니다.Float32에서 동작합니다.어레이 및 Float64반올림 부작용이 없는 배열.+0과 -0은 다릅니다.무한은 같다.사일런트 Na는 동일합니다.시그널링 NaN은 신호에 따라 다릅니다(vanilla JavaScript에서는 볼 수 없습니다).
객체를 키로 사용하여 지도의 값을 얻고자 하는 Google에서 이 질문을 발견한 사람에게:
경고: 이 답변은 모든 개체에서 작동하지 않습니다.
var map = new Map<string,string>();
map.set(JSON.stringify({"A":2} /*string of object as key*/), "Worked");
console.log(map.get(JSON.stringify({"A":2}))||"Not worked");
출력:
일했다
언급URL : https://stackoverflow.com/questions/29759480/how-to-customize-object-equality-for-javascript-set
'source' 카테고리의 다른 글
JSON 키 이름에 유효한 문자와 유효하지 않은 문자는 무엇입니까? (0) | 2023.01.19 |
---|---|
CLI를 사용하여 Wildfly에서 MariaDB 데이터 소스를 설정하는 방법 (0) | 2023.01.19 |
변수를 사용하는 인덱스가 있는 경우 MySQL 순위 지정 (0) | 2023.01.19 |
MySQL에서 utf8 깨진 문자 감지 (0) | 2023.01.19 |
MySQL JOIN은 가장 최근의 행만 사용하시겠습니까? (0) | 2023.01.19 |