@import "CPMutableSet.j" var hasOwnProperty = Object.prototype.hasOwnProperty; /*! @class CPSet @ingroup foundation @brief An unordered collection of objects. */ @implementation _CPConcreteMutableSet : CPMutableSet { Object _contents; unsigned _count; } /* Initializes a newly allocated set with members taken from the specified list of objects. @param objects A array of objects to add to the new set. If the same object appears more than once objects, it is added only once to the returned set. @param count The number of objects from objects to add to the new set. */ - (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount { self = [super initWithObjects:objects count:aCount]; if (self) { _count = 0; _contents = { }; var index = 0, count = MIN([objects count], aCount); for (; index < count; ++index) [self addObject:objects[index]]; } return self; } - (CPUInteger)count { return _count; } - (id)member:(id)anObject { var UID = [anObject UID]; if (hasOwnProperty.call(_contents, UID)) return _contents[UID]; else { for (var objectUID in _contents) { if (!hasOwnProperty.call(_contents, objectUID)) continue; var object = _contents[objectUID]; if (object === anObject || [object isEqual:anObject]) return object; } } return nil; } - (CPArray)allObjects { var array = [], property; for (property in _contents) { if (hasOwnProperty.call(_contents, property)) array.push(_contents[property]); } return array; } - (CPEnumerator)objectEnumerator { return [[self allObjects] objectEnumerator]; } /* Adds a given object to the receiver. @param anObject The object to add to the receiver. */ - (void)addObject:(id)anObject { if (anObject == nil) [CPException raise:CPInvalidArgumentException reason:@"attempt to insert nil or undefined"]; if ([self containsObject:anObject]) return; _contents[[anObject UID]] = anObject; _count++; } /* Removes a given object from the receiver. @param anObject The object to remove from the receiver. */ - (void)removeObject:(id)anObject { // Removing nil is an error. if (anObject == nil) [CPException raise:CPInvalidArgumentException reason:@"attempt to remove nil or undefined"]; // anObject might be isEqual: another object in the set. We need the exact instance so we can remove it by UID. var object = [self member:anObject]; // ...but removing an object not present in the set is not an error. if (object != nil) { delete _contents[[object UID]]; _count--; } } /* Performance improvement. */ - (void)removeAllObjects { _contents = {}; _count = 0; } - (Class)classForCoder { return [CPSet class]; } @end // This makes all CPSet instances iterable using for...of or the spread operator. // // This implementation is 'lazy' and efficient, iterating directly over the // internal _contents object rather than creating a temporary array. // _CPConcreteMutableSet.allocator.prototype[Symbol.iterator] = function*() { // 'this._contents' is the internal JavaScript object used for storage. // The keys are object UIDs and the values are the objects themselves. const contents = this._contents; // A 'for...in' loop iterates over the keys (UIDs) of the contents object. // This is a lazy operation and does not create an intermediate array. for (const key in contents) { // It's crucial to check that the key is an own property and not // one inherited from Object.prototype. if (Object.prototype.hasOwnProperty.call(contents, key)) { // Yield the VALUE, which is the actual object stored in the set. yield contents[key]; } } };