[javascript] JavaScript에서 세트를 모방합니까?

JavaScript로 일하고 있습니다. 다음 속성을 사용하여 정렬되지 않은 고유 한 문자열 값 목록을 저장하고 싶습니다 .

  1. ‘목록에 A’가 있는지 묻는 가장 빠른 방법은 무엇입니까?
  2. ‘목록에 A가있는 경우 목록에서 A를 삭제’하는 빠른 방법
  3. ‘목록에 A를 아직 추가하지 않은 경우 A’를 추가하는 빠른 방법입니다.

내가 정말로 원하는 것은 세트입니다. JavaScript에서 세트를 모방하는 가장 좋은 방법에 대한 제안 사항이 있습니까?

질문은 속성을 저장하는 키와 값이 모두 true로 설정된 Object를 사용하는 것이 좋습니다 . 합리적인 방법입니까?



답변

ES6 가능 환경 (예 : node.js, 필요한 ES6 기능이있는 특정 브라우저 또는 환경에 대한 ES6 코드를 변환하는 특정 브라우저)에서 프로그래밍하는 경우 ES6에 Set내장 된 객체를 사용할 수 있습니다 . 그것은 매우 좋은 기능을 가지고 있으며 귀하의 환경에서 그대로 사용할 수 있습니다.


ES5 환경의 많은 간단한 것들에 대해 Object를 사용하면 매우 효과적입니다. 경우 obj개체이며, A당신이 세트에 조작하려는 값을 갖는 변수이다, 당신은이 작업을 수행 할 수 있습니다 :

초기화 코드 :

// create empty object
var obj = {};

// or create an object with some items already in it
var obj = {"1":true, "2":true, "3":true, "9":true};

질문 1 :A목록에 :

if (A in obj) {
    // put code here
}

질문 2 : 목록에 ‘A’가 있으면 삭제하십시오.

delete obj[A];

질문 3 : 목록에 ‘A’가 없으면 목록에 추가하십시오

obj[A] = true;

완전성 A을 위해 목록 에 있는지 여부에 대한 테스트 는 다음과 같이 조금 더 안전합니다.

if (Object.prototype.hasOwnProperty.call(obj, A))
    // put code here
}

기본 객체의 내장 메소드 및 / 또는 속성과 속성이 충돌 할 가능성이 있기 때문입니다 constructor.


ES6의 사이드 바 : ECMAScript 6 의 현재 작업 버전 또는 ES 2015라는 항목에는 내장 Set 객체가 있습니다. 현재 일부 브라우저에서 구현되었습니다. 브라우저 가용성이 시간이 지남에 따라 변화하기 때문에 들어 줄에서 볼 수 Set있는 이 ES6 호환성 테이블 브라우저 가용성의 현재 상태를 볼 수 있습니다.

내장 Set 객체의 한 가지 장점은 Object와 같이 모든 키를 문자열로 강제 변환하지 않으므로 별도의 키로 5와 “5”를 모두 가질 수 있다는 것입니다. 또한 문자열 변환없이 세트에서 직접 객체를 사용할 수도 있습니다. 다음 은 Set 개체에 대한 일부 기능 및 MDN 설명서 를 설명 하는 기사 입니다 .

이제 ES6 세트 객체에 대한 폴리 필을 작성 했으므로 이제 사용할 수 있으며 브라우저가 지원하는 경우 내장 세트 객체를 자동으로 연기합니다. 이것은 ES7 호환 코드를 작성하여 IE7까지 계속 작동한다는 이점이 있습니다. 그러나 몇 가지 단점이 있습니다. ES6 세트 인터페이스는 ES6 이터레이터를 활용하므로 원하는 작업을 수행 할 수 for (item of mySet)있으며 세트를 통해 자동으로 반복됩니다. 그러나 이러한 유형의 언어 기능은 폴리 필을 통해 구현할 수 없습니다. 새로운 ES6 언어 기능을 사용하지 않고 ES6 세트를 계속 반복 할 수 있지만 솔직히 새로운 언어 기능이 없으면 아래에 포함 된 다른 세트 인터페이스만큼 편리하지 않습니다.

둘 다보고 나에게 가장 적합한 것을 결정할 수 있습니다. ES6 세트 폴리 필은 https://github.com/jfriend00/ES6-Set에 있습니다.

참고로, 자체 테스트에서 Firefox v29 Set 구현이 현재 사양의 초안에서 완전히 최신 상태가 아님을 알았습니다. 예를 들어 .add()사양 설명 및 내 polyfill 지원과 같은 메소드 호출 을 연결할 수 없습니다 . 이것은 아직 확정되지 않았기 때문에 동작 사양의 문제 일 수 있습니다.


사전 빌드 세트 오브젝트 : 모든 브라우저에서 사용할 수있는 세트에서 조작하기위한 메소드가있는 이미 빌드 된 오브젝트를 원하는 경우 다른 유형의 세트를 구현하는 일련의 다른 사전 빌드 된 오브젝트를 사용할 수 있습니다. set 객체의 기본을 구현하는 작은 코드 인 miniSet이 있습니다. 또한 더 많은 기능이 풍부한 세트 객체와 사전 (각 키의 값을 저장 / 검색 할 수 있음) 및 객체 세트 (JS 객체 또는 DOM 객체 중 하나를 제공하는 DOM 객체)를 포함하여 여러 파생물이 있습니다. 각각 고유의 키를 생성하는 함수 또는 ObjectSet이 키를 생성합니다).

다음은 miniSet 코드의 사본입니다 (가장 최신 코드는 github에 있습니다 ).

"use strict";
//-------------------------------------------
// Simple implementation of a Set in javascript
//
// Supports any element type that can uniquely be identified
//    with its string conversion (e.g. toString() operator).
// This includes strings, numbers, dates, etc...
// It does not include objects or arrays though
//    one could implement a toString() operator
//    on an object that would uniquely identify
//    the object.
// 
// Uses a javascript object to hold the Set
//
// This is a subset of the Set object designed to be smaller and faster, but
// not as extensible.  This implementation should not be mixed with the Set object
// as in don't pass a miniSet to a Set constructor or vice versa.  Both can exist and be
// used separately in the same project, though if you want the features of the other
// sets, then you should probably just include them and not include miniSet as it's
// really designed for someone who just wants the smallest amount of code to get
// a Set interface.
//
// s.add(key)                      // adds a key to the Set (if it doesn't already exist)
// s.add(key1, key2, key3)         // adds multiple keys
// s.add([key1, key2, key3])       // adds multiple keys
// s.add(otherSet)                 // adds another Set to this Set
// s.add(arrayLikeObject)          // adds anything that a subclass returns true on _isPseudoArray()
// s.remove(key)                   // removes a key from the Set
// s.remove(["a", "b"]);           // removes all keys in the passed in array
// s.remove("a", "b", ["first", "second"]);   // removes all keys specified
// s.has(key)                      // returns true/false if key exists in the Set
// s.isEmpty()                     // returns true/false for whether Set is empty
// s.keys()                        // returns an array of keys in the Set
// s.clear()                       // clears all data from the Set
// s.each(fn)                      // iterate over all items in the Set (return this for method chaining)
//
// All methods return the object for use in chaining except when the point
// of the method is to return a specific value (such as .keys() or .isEmpty())
//-------------------------------------------


// polyfill for Array.isArray
if(!Array.isArray) {
    Array.isArray = function (vArg) {
        return Object.prototype.toString.call(vArg) === "[object Array]";
    };
}

function MiniSet(initialData) {
    // Usage:
    // new MiniSet()
    // new MiniSet(1,2,3,4,5)
    // new MiniSet(["1", "2", "3", "4", "5"])
    // new MiniSet(otherSet)
    // new MiniSet(otherSet1, otherSet2, ...)
    this.data = {};
    this.add.apply(this, arguments);
}

MiniSet.prototype = {
    // usage:
    // add(key)
    // add([key1, key2, key3])
    // add(otherSet)
    // add(key1, [key2, key3, key4], otherSet)
    // add supports the EXACT same arguments as the constructor
    add: function() {
        var key;
        for (var i = 0; i < arguments.length; i++) {
            key = arguments[i];
            if (Array.isArray(key)) {
                for (var j = 0; j < key.length; j++) {
                    this.data[key[j]] = key[j];
                }
            } else if (key instanceof MiniSet) {
                var self = this;
                key.each(function(val, key) {
                    self.data[key] = val;
                });
            } else {
                // just a key, so add it
                this.data[key] = key;
            }
        }
        return this;
    },
    // private: to remove a single item
    // does not have all the argument flexibility that remove does
    _removeItem: function(key) {
        delete this.data[key];
    },
    // usage:
    // remove(key)
    // remove(key1, key2, key3)
    // remove([key1, key2, key3])
    remove: function(key) {
        // can be one or more args
        // each arg can be a string key or an array of string keys
        var item;
        for (var j = 0; j < arguments.length; j++) {
            item = arguments[j];
            if (Array.isArray(item)) {
                // must be an array of keys
                for (var i = 0; i < item.length; i++) {
                    this._removeItem(item[i]);
                }
            } else {
                this._removeItem(item);
            }
        }
        return this;
    },
    // returns true/false on whether the key exists
    has: function(key) {
        return Object.prototype.hasOwnProperty.call(this.data, key);
    },
    // tells you if the Set is empty or not
    isEmpty: function() {
        for (var key in this.data) {
            if (this.has(key)) {
                return false;
            }
        }
        return true;
    },
    // returns an array of all keys in the Set
    // returns the original key (not the string converted form)
    keys: function() {
        var results = [];
        this.each(function(data) {
            results.push(data);
        });
        return results;
    },
    // clears the Set
    clear: function() {
        this.data = {};
        return this;
    },
    // iterate over all elements in the Set until callback returns false
    // myCallback(key) is the callback form
    // If the callback returns false, then the iteration is stopped
    // returns the Set to allow method chaining
    each: function(fn) {
        this.eachReturn(fn);
        return this;
    },
    // iterate all elements until callback returns false
    // myCallback(key) is the callback form
    // returns false if iteration was stopped
    // returns true if iteration completed
    eachReturn: function(fn) {
        for (var key in this.data) {
            if (this.has(key)) {
                if (fn.call(this, this.data[key], key) === false) {
                    return false;
                }
            }
        }
        return true;
    }
};

MiniSet.prototype.constructor = MiniSet;


답변

당신은 같은 속성없이 객체를 만들 수 있습니다

var set = Object.create(null)

이것은 세트 역할을 할 수 있고 사용할 필요가 없습니다 hasOwnProperty.


var set = Object.create(null); // create an object with no properties

if (A in set) { // 1. is A in the list
  // some code
}
delete set[a]; // 2. delete A from the list if it exists in the list 
set[A] = true; // 3. add A to the list if it is not already present


답변

ECMAScript 6부터 Set 데이터 구조는 기본 제공 기능 입니다. node.js 버전과의 호환성은 여기 에서 찾을 수 있습니다 .


답변

자바 스크립트의 ES6 버전에서는, 형태에 구축 세트 ( 브라우저와 체크 호환성 ).

var numbers = new Set([1, 2, 4]); // Set {1, 2, 4}

세트에 요소추가 하려면 간단히을 사용 합니다.이 요소는 .add()실행되어 O(1)설정 요소를 추가하거나 (존재하지 않는 경우) 이미 존재하는 경우 아무 것도 수행하지 않습니다. 거기에 모든 유형의 요소를 추가 할 수 있습니다 (배열, 문자열, 숫자)

numbers.add(4); // Set {1, 2, 4}
numbers.add(6); // Set {1, 2, 4, 6}

집합 의 요소 수확인 하려면을 사용하면됩니다 .size. 또한 실행O(1)

numbers.size; // 4

세트에서 요소제거 하려면을 사용하십시오 .delete(). 값이 있고 제거 된 경우 true를 반환하고 값이 없으면 false를 반환합니다. 에서 실행됩니다 O(1).

numbers.delete(2); // true
numbers.delete(2); // false

요소가 세트에 있는지 여부확인 하려면 요소가 세트에 .has()있으면 true를, 그렇지 않으면 false를 리턴합니다. 에서 실행됩니다 O(1).

numbers.has(3); // false
numbers.has(1); // true

원하는 방법 외에도 몇 가지 추가 방법이 있습니다.

  • numbers.clear(); 세트에서 모든 요소를 ​​제거합니다.
  • numbers.forEach(callback); 삽입 순서로 세트의 값을 반복
  • numbers.entries(); 모든 값의 반복자를 생성
  • numbers.keys(); 와 같은 세트의 키를 돌려줍니다 numbers.values()

객체 유형 값만 추가 할 수있는 약점도 있습니다.


답변

현재 숫자와 문자열에서 잘 작동하는 Sets 구현을 시작했습니다. 나의 주요 초점은 차이 연산에 있었고, 나는 그것을 가능한 한 효율적으로 만들려고 노력했다. 포크와 코드 리뷰는 환영합니다!

https://github.com/mcrisc/SetJS


답변

방금 d3.js 라이브러리에 세트, 맵 및 기타 데이터 구조가 구현되어 있음을 알았습니다. 나는 그들의 효율성에 대해 논쟁 할 수 없지만 그것이 인기있는 도서관이라는 사실로 판단하면 그것이 필요한 것이어야합니다.

설명서는 여기

편의상 링크에서 복사합니다 (처음 3 가지 기능이 관심 대상입니다)


  • d3.set ([배열])

새로운 세트를 구축합니다. array가 지정되면 주어진 문자열 값의 배열을 리턴 된 세트에 추가합니다.

  • set.has (값)

이 세트에 지정된 값 캐릭터 라인의 엔트리가있는 경우에 한정해 true를 리턴합니다.

  • set.add (값)

지정된 값 문자열을이 세트에 추가합니다.

  • set.remove (값)

세트에 지정된 값 문자열이 포함 된 경우이를 제거하고 true를 리턴합니다. 그렇지 않으면이 메소드는 아무 것도 수행하지 않고 false를 리턴합니다.

  • set.values ​​()

이 세트의 캐릭터 라인 치의 배열을 돌려줍니다. 반환 된 값의 순서는 임의적입니다. 문자열 집합의 고유 한 값을 계산하는 편리한 방법으로 사용할 수 있습니다. 예를 들면 다음과 같습니다.

d3.set ([ “foo”, “bar”, “foo”, “baz”]). values ​​(); // “foo”, “bar”, “baz”

  • set.forEach (기능)

이 세트의 각 값에 대해 지정된 함수를 호출하여 값을 인수로 전달합니다. 이 기능의 컨텍스트는이 세트입니다. 미정 도리를 돌려줍니다. 반복 순서는 임의적입니다.

  • set.empty ()

이 세트에 제로의 값이있는 경우에 한정해 true를 리턴합니다.

  • set.size ()

이 세트의 값수를 돌려줍니다.


답변

그렇습니다. 이는 합리적인 방법입니다. 이것은 모든 것이 (이 경우에는 잘 사용됩니다) 직접 액세스 할 수있는 많은 키 / 값입니다.

추가하기 전에 이미 존재하는지 확인해야합니다. 또는 존재를 나타내기만하면 “추가”하면 실제로 아무것도 변경되지 않으며 개체에 다시 설정됩니다.