用JavaScript模仿套装? [英] Mimicking sets in JavaScript?

查看:117
本文介绍了用JavaScript模仿套装?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用JavaScript。我想存储唯一,无序字符串值的列表,其中包含以下属性:

I'm working in JavaScript. I'd like to store a list of unique, unordered string values, with the following properties:


  1. a快速询问'是否在列表中的A'?

  2. 如果列表中存在'从列表中删除A'的快速方法'

  3. 快速的方法'如果它不存在则将'添加到列表'。

我真正想要的是一个组。有关在JavaScript中模仿集合的最佳方法的任何建议吗?

What I really want is a set. Any suggestions for the best way to mimic a set in JavaScript?

问题建议使用对象,键存储属性,值都设置为true:这是一种明智的方法吗?

This question recommends using an Object, with the keys storing properties, and the values all set to true: is that a sensible way?

推荐答案

如果您在支持ES6的环境中编程(例如node.js,具有您需要的ES6功能的特定浏览器或为您的环境转换ES6代码),那么您可以使用 设置对象构建进入ES6 。它具有非常好的功能,可以在您的环境中使用。

If you are programming in an ES6-capable environment (such as node.js, a specific browser with the ES6 capabilities you need or transpiling ES6 code for your environment), then you can use the Set object built into ES6. It has very nice capabilities and can be used as is right in your environment.

对于ES5环境中的许多简单事物,使用Object非常有效。如果 obj 是您的对象而 A 是一个变量,它具有您要在集合中操作的值,那么你可以这样做:

For many simple things in an ES5 environment, using an Object works very well. If obj is your object and A is a variable that has the value you want to operate on in the set, then you can do these:

初始化代码:

// 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',它在那里:

Question 2: Delete 'A' from the list if it's there:

delete obj[A];

问题3:如果'不是',请在列表中添加'A'已经存在

Question 3: Add 'A' to the list if it wasn't already there

obj[A] = true;






为了完整性,测试是否<$ c列表中的$ c> A 对此更安全:

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

因为基础对象上的内置方法和/或属性之间可能存在冲突,例如构造函数属性。

because of potential conflict between built-in methods and/or properties on the base Object like the constructor property.

ES6上的补充工具栏 ECMAScript 6 的当前工作版本或称为ES 2015的东西内置的Set对象。它现在在一些浏览器中实现。由于浏览器可用性随时间而变化,您可以在 Set 的行。 >此ES6兼容性表,以查看浏览器可用性的当前状态。

Sidebar on ES6: The current working version of ECMAScript 6 or somethings called ES 2015 has a built-in Set object. It is implemented now in some browsers. Since browser availability changes over time, you can look at the line for Set in this ES6 compatibility table to see the current status for browser availability.

内置Set对象的一个​​优点是它不具备将所有键强制转换为像对象一样的字符串,这样你就可以将5和5分别作为单独的键。并且,您甚至可以直接在集合中使用对象而无需字符串转换。这是一篇文章,介绍了一些功能并在Set对象上 MDN的文档

One advantage of the the built-in Set object is that it doesn't coerce all keys to a string like the Object does so you can have both 5 and "5" as separate keys. And, you can even use Objects directly in the set without a string conversion. Here's an article that describes some of the capabilities and MDN's documentation on the Set object.

我现在已经为ES6设置对象编写了一个polyfill,所以你现在可以开始使用它,如果浏览器支持它,它会自动推迟到内置的set对象。这样做的好处是,您正在编写ES6兼容代码,这些代码可以一直回到IE7。但是,有一些缺点。 ES6 set接口利用了ES6迭代器,因此您可以执行 for(mySet的项目)之类的操作,它将自动为您迭代该集合。但是,这种语言功能无法通过polyfill实现。您仍然可以在不使用新的ES6语言功能的情况下迭代ES6集,但坦率地说,如果没有新的语言功能,它就不如我在下面包含的其他设置界面那么方便。

I have now written a polyfill for the ES6 set object so you could start using that now and it will automatically defer to the built-in set object if the browser supports it. This has the advantage that you're writing ES6 compatible code that will work all the way back to IE7. But, there are some downsides. The ES6 set interface takes advantage of the ES6 iterators so you can do things like for (item of mySet) and it will automatically iterate through the set for you. But, this type of language feature cannot be implemented via polyfill. You can still iterate an ES6 set without using the new ES6 languages features, but frankly without the new language features, it isn't as convenient as the other set interface I include below.

在查看两者后,您可以决定哪一个最适合您。 ES6 set polyfill位于: https://github.com/jfriend00/ES6-Set

You can decide which one works best for you after looking at both. The ES6 set polyfill is here: https://github.com/jfriend00/ES6-Set.

仅供参考,在我自己的测试中,我注意到Firefox v29 Set实现并不完全是最新的规范草案。例如,您不能链接 .add()方法调用,如spec描述和我的polyfill支持。这可能是运动规范的问题,因为它还没有最终确定。

FYI, in my own testing, I've noticed that the Firefox v29 Set implementation is not fully up-to-date on the current draft of the spec. For example, you can't chain .add() method calls like the spec describes and my polyfill supports. This is probably a matter of a specification in motion as it is not yet finalized.

预制套装对象:如果您希望已构建的对象具有可在任何浏览器中使用的集合上操作的方法,则可以使用一系列实现不同类型集合的不同预构建对象。有一个miniSet,它是实现set对象基础的小代码。它还有一个功能更丰富的set对象和几个派生,包括Dictionary(让你存储/检索每个键的值)和一个ObjectSet(让你保留一组对象 - JS对象或DOM对象,你要么提供为每个生成唯一键的函数或ObjectSet将为您生成密钥。)

Pre-Built Set objects: If you want an already built object that has methods for operating on a set that you can use in any browser, you can use a series of different pre-built objects that implement different types of sets. There is a miniSet which is small code that implements the basics of a set object. It also has a more feature rich set object and several derivations including a Dictionary (let's you store/retrieve a value for each key) and an ObjectSet (let's you keep a set of objects - either JS objects or DOM objects where you either supply the function that generates a unique key for each one or the ObjectSet will generate the key for you).

这是miniSet代码的副本(最多为日期代码为此处于github上。)

Here's a copy of the code for the miniSet (most up-to-date code is here on 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;

这篇关于用JavaScript模仿套装?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆