Basically I have been using a Dictionary object in my program that basically took ints as its keys and stored RTMFP peer IDs in the appropriate locations. Each int was unique and represented one user.
Now I'm needing to expand on this where users are identified by a combination of the int and a Boolean value, kind of like this:
private var m_iUID:int;
private var m_blnIsCurrent:Boolean;
Only the combination between those two really uniquely identifies the user. That being said I was just about to use a new class made out of this for the Dictionary keys; but then it occurred to me that instead of doing it this way, I could just add the peer ID to the class definition and turn the Dictionary object into an ArrayCollection:
private var m_iUID:int;
private var m_blnIsCurrent:Boolean;
public var m_strNearID:String;
So now I'm wondering which is really better in this scenario. And that question has led to a bigger question: where do you really draw the line between these two collection types in general? They're suddenly starting to not seem all that different after all, except where you're trying to avoid messing with class definitions. I guess I'm really asking for advice about both the specific scenario and the general question. Thanks!
ArrayCollection is just a wrapper for an Array, and is only available in Flex.
In AS3 you really have 3 fundamental hash table types: Array, Object, and Dictionary. You choose which one to use based on the type of key you want to use: an integer, a string, or an object reference. Arrays will convert any key to an int, Object will convert any key to a string. Dictionary works like Object for string keys (and will convert primitives to a string) but what it is really good at is using object references as keys.
It you want to use a single int as the unique key, use an array. If you want to use a single string as the unique key, use an object. If you want to use object references as the unique key, use a Dictionary.
In your case you should probably use an Object, and a custom toString() method on your "key" class. This is because you want to use a composite of primitive values (NOT an object reference) as your unique key. There is no way to do this natively, so you'll have to mash the values together as a single string. Objects are the best (fastest) hash table for string keys, so that is the collection you should use.
Example:
class User {
private var m_iUID:int;
private var m_blnIsCurrent:Boolean;
public var m_strNearID:String;
public function User(UID:int, IsCurrent:Boolean) {
m_iUID = UID;
m_blnIsCurrent = IsCurrent;
}
// Custom toString to mash together primitives
public function toString() {
return m_iUID.toString() + "-" + (m_blnIsCurrent ? "1" : "0");
}
}
// Later:
var allUsers:Object = {}
var user1:User = new User(231049, true);
var user2:User = new User(0x2309, false);
// Implicitly calls toString():
allUsers[user1] = "User 1";
allUsers[user2] = "User 2";
// All of the following will successfully retrieve the value for user1 ("User 1"):
// ONLY the first would work if allUsers was a Dictionary
trace(allUsers[user1]);
trace(allUsers[user1.toString()]);
trace(allUsers["231049-1"]);
trace(allUsers[new User(231049, true)]);
Dictionary and ArrayCollection have some important differences:
Dictionary maps objects to other objects, while ArrayCollection is just a list of objects.
ArrayCollection is Flex only, so unusable in a generic AS3 project.
Which one you should use really depends on what you need in your app:
Will you be using the "identity" object (with user id and "is current") somewhere else, without an associated peer id? In that case, make it a separate Identity class or so and use a Dictionary to map Identity instances to peer ids.
Do you need to perform lookups based on identities? In other words, do you need to ask "which peer id is associated with this identity?". If so, go for Dictionary + Identity once more, to avoid looping through a list instead.
I'm sure there are more considerations, but these should get you started.
Related
There is already some questions about map and weak maps, like this: What's the difference between ES6 Map and WeakMap? but I would like to ask in which situation should I favor the use of these data structures? Or what should I take in consideration when I favor one over the others?
Examples of the data structures from:https://github.com/lukehoban/es6features
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set
Bonus. Which of the above data structures will produce the same/similar result of doing: let hash = object.create(null); hash[index] = something;
This is covered in ยง23.3 of the specification:
If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap.
So the entries in a weak map, if their keys aren't referenced by anything else, will be reclaimed by garbage collection at some point.
In contrast, a Map holds a strong reference to its keys, preventing them from being garbage-collected if the map is the only thing referencing them.
MDN puts it like this:
The key in a WeakMap is held weakly. What this means is that, if there are no other strong references to the key, then the entire entry will be removed from the WeakMap by the garbage collector.
And WeakSet does the same.
...in which situation should I favor the use of this data structures?
Any situation where you don't want the fact you have a map/set using a key to prevent that key from being garbage-collected. Here are some examples:
Having instance-specific information which is truly private to the instance, which looks like this: (Note: This example is from 2015, well before private fields were an option. Here in 2021, I'd use private fields for this.)
let Thing = (() => {
var privateData = new WeakMap();
class Thing {
constructor() {
privateData[this] = {
foo: "some value"
};
}
doSomething() {
console.log(privateData[this].foo);
}
}
return Thing;
})();
There's no way for code outside that scoping function to access the data in privateData. That data is keyed by the instance itself. You wouldn't do that without a WeakMap because it would be a memory leak, your Thing instances would never be cleaned up. But WeakMap only holds weak references, and so if your code using a Thing instance is done with it and releases its reference to the instance, the WeakMap doesn't prevent the instance from being garbage-collected; instead, the entry keyed by the instance is removed from the map.
Holding information for objects you don't control. Suppose you get an object from some API and you need to remember some additional information about that object. You could add properties to the object itself (if it's not sealed), but adding properties to objets outside of your control is just asking for trouble. Instead, you can use a WeakMap keyed by the object to store your extra information.
One use case for WeakSet is tracking or branding: Suppose that before "using" an object, you need to know whether that object has ever been "used" in the past, but without storing that as a flag on the object (perhaps because if it's a flag on the object, other code can see it [though you could use a private field to prevent that]; or because it's not your object [so private fields wouldn't help]). For instance, this might be some kind of single-use access token. A WeakSet is a simple way to do that without forcing the object to stay in memory.
Which of the above data structures will produce the same/similar result of doing: let hash = Object.create(null); hash[index] = something;
That would be nearest to Map, because the string index (the property name) will be held by a strong reference in the object (it and its associated property will not be reclaimed if nothing else references it).
I do not have any specific id available in the response JSON body (I can not change the body). That is why I can not use
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:....
mapping.identificationAttributes = #[#"specificId"];
Is it possible to configure the mapping in such a way that there is no new NSManagedObject created but always the previous one is updated, if such object exists?
I would like to fetch data for ui update from a single response object (of specific class). Yes I can delete the previous instance of the response before the new one is received but the approach required in this question is cleaner and I do not need to keep the reference/id to the response entity.
I am reading the documentation for RKManagedObjectRequestOperation but it is not clear whether this approach is supported by Restkit.
Thank you for comment.
I have made a hack that is not acceptable but it works: I am using a special attribute in each special "singleton" NSManagedObject's subclass: e.g. unique which I use for the identification on the class level:
In RKManagedObjectMappingOperationDataSource there is the condition modified to allow passing entities with the special unique attribute:
// If we have found the entity identification attributes, try to find an existing instance to update
if ([entityIdentifierAttributes count] || [self.managedObjectCache isUniqueEntityClass:entity])...
In RKFetchRequestManagedObjectCache and in RKInMemoryManagedObjectCache there is the new method defined:
- (BOOL) isUniqueEntityClass:(NSEntityDescription*)entity {
__block BOOL isUniqueEntityClass = NO;
[[[entity attributesByName] allKeys] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
isUniqueEntityClass = [obj isEqualToString:#"unique"];
if(isUniqueEntityClass) {
*stop = YES;
return;
}
}];
return isUniqueEntityClass;
}
In the method
- (NSSet *)managedObjectsWithEntity:(NSEntityDescription *)entity
attributeValues:(NSDictionary *)attributeValues
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext...
isUniqueEntityClass decides if we should use predicate based on attributeValues to fetch the entity or directly fetch the entity without the predicate.
I am learning programming abstract data types. Trying to build custom hash table based dict.
SO far I've created a class place holder.
public class HashMapDict implements IDict
{
private var _map:Array;
public function HashMapDict()
{
_map = new Array();
//TODO: implement function
}
public function set(keys:Array):Boolean
{
// 1. For each key in array of keys
// 2. Pass Key.key to hash function
// 3. Write Key to _map[hash(Key.key)]
return true;
}
}
I see the main method set doing the following
// 1. For each key in array of keys
// 2. Pass Key.key to hash function
// 3. Write Key to _map[hash(Key.key)]
What I am thinking about is to use cryptography libs for hash generation. But I am a bit confused with how it should work. e.g. Tried to look on several libs like as3crypto (http://crypto.hurlant.com/demo/) and it seems to produce hash in a way I don't really think can be used for indexes in arrays.
E.g.
http://screencast.com/t/bE1lYQEqp4D
Can you advise which lib can I use to generate usable hashes? and how should they look like
Just as a heads up -- I can almost guarantee that you will not be able to make something better than Dictionary or even Object at this. Your proposed plan could work, but it would offer no benefit over these. I also feel compelled to suggest Vector over Array as Vectors are faster and more powerful.
The problem with Hash libs is that they generally result in very, very large numbers. MD5, for example, will produce a hex string which represents far more than what can fit even into a uint (uint in as can fit 2 ^ 32, MD5 is 2^128). This also happens to be the maximum size of an Array/Vector in AS.
This isn't to say that they can't fit into Number (which can hold about 1.79*10^308), but it does mean that you'll lose the benefit of numeric indexing and you certainly won't get much benefit from Vectors at that point. You'll basically be falling back on Object.
To be honest, it really does look like you have one of two options. Either you can implement a direct lookup using a second Array/Vector. This has the problem of being O(n) lookup time while the lookup time of a Hash table would be O(1).
It seems, at least to me, that you'll need to use Dictionary or Object no matter what to get this done.
For implementation of a hash table, a cryptographic hash function is overkill.
Use this only if you are concerned with an attack of someone who tries to feed you bad data (e.g. keys with lots of hash collisions) to make the hash table slow.
For a hash table use, a hash function like the following one is enough (pseudocode, as I don't know the right syntax):
hash = 0
for c in string:
hash = hash * 13 + c;
return hash
But as other answers said, there is already a hash table built in, and you don't really need to reimplement it.
I might be missing something, but I think you should look at flash.utils::Dictionary.
It makes hashing obsolete. If you must have some sort of primitive key, I suggest using the following:
class UIDUtil {
static private var map:Dictionary = new Dictionary(true);
static private var counter:int = 0;
static public function getUID(value:*):int {
return map[value] ||= counter++;
}
}
But your class I would implement as:
public class HashMapDict implements IDict {
private var _map:Dictionary = new Dictionary();
public function set(keys:Array):Boolean {
for each (var key:* in keys) _map[key] = key;
return true;
}
}
I am not sure of its purpose though ;)
I want to have a collection of objects, which will be of a class I created called Server. A Server has a string property which is it's IP address, as well as many other pieces of data and objects.
I will have methods for adding and removing servers to this collection, and there will be a need to find a server by it's IP address occasionally. If I were doing this in C# I would use a Dictionary< where the IP string would be the key and the Server object would be the value. I could easily check to see if an item exists in the Dictionary before attempting to add it.
So my requirements are:
1. Ability to add items to the collection (I don't care where they go, front, back, middle)
2. Ability to remove items from anywhere in the collection.
3. Ability to determine if a particular IP address already exists in the collection.
4. Ability to get a reference to a Server object by it's IP.
Edit: Oh yes, I would like it to be strongly typed like the Vector... I guess it's not absolutely necesary, but would be nice.
So it seems like an associative arrays will give me what I need, except I'm not sure about how to do #3 or #4.
public var Servers:Object = new Object( );
public function AddServer(server:Server):void
{
//TODO:need to check if it exists first and throw an error if so
//(it's the caller's responsibility to call DoesServerExist first)
Servers[server.IP] = server;
}
public function RemoveServer(IP:string):void
{
//is it OK to attempt to delete an item if it doesn't already exist?
//do I need to check if it exists before doing delete?
delete Servers[IP];
}
public function DoesServerExist(IP:string):bool
{
//Do I loop through all the elements testing it's IP property?
//Or can I just do something like this?
if(Servers[IP] == null)
{
return false;
}
else
{
return true;
}
}
public function GetServer(IP:string):Server
{
return Servers[IP];//what is returned if this IP doesn't exist?
}
Call me goofy, but why not use the Dictionary class? That gets you everything except strong typing.
If you want strong typing then I'd say you need a custom container, which wraps up a Vector of Servers, and a Dictionary or associative array of IP strings that indexes into the Vector. Then you'd need to expose methods for access, test, insert and remove.
You can just use an array. Example:
var dict:Array = [];
var ip = "164.157.012.122"
dict[ip] = "Server name"
if (dict[ip] == "Server name"){
trace("Yay");
}
//membership
if (dict[ip]){
trace(ip + " is a member of dict");
} else {
trace (ip + " is not a member");
}
//removal:
dict[ip] = null;
AS3 does not really have a built in Dictionary class, unfortunately.
I've been spending some of my spare time working a set of collections for ActionScript 3 but I've hit a pretty serious roadblock thanks for the way ActionScript 3 handles equality checks inside Dictionary Objects.
When you compare a key in a dictionary, ActionScript uses the === operator to perform the comparison, this has a bit of a nasty side effect whereby only references to the same instance will resolve true and not objects of equality. Here's what I mean:
const jonny1 : Person = new Person("jonny", 26);
const jonny2 : Person = new Person("jonny", 26);
const table : Dictionary = new Dictionary();
table[jonny1] = "That's me";
trace(table[jonny1]) // traces: "That's me"
trace(table[jonny2]) // traces: undefined.
The way I am attempting to combat this is to provide an Equalizer interface which looks like this:
public interface Equalizer
{
function equals(object : Object) : Boolean;
}
This allows to to perform an instanceOf-esq. check whenever I need to perform an equality operation inside my collections (falling back on the === operator when the object doesn't implement Equalizer); however, this doesn't get around the fact that my underlying datastructure (the Dictionary Object) has no knowledge of this.
The way I am currently working around the issue is by iterating through all the keys in the dictionary and performing the equality check whenever I perform a containsKey() or get() operation - however, this pretty much defeats the entire point of a hashmap (cheap lookup operations).
If I am unable to continue using a Dictionary instance as the backing for map, how would I go about creating the hashes for unique object instances passed in as keys so I can still maintain equality?
How about you compute a hash code for your objects when you insert them, and then look them up by the hash code in your backing dictionary? The hashcode should compare === just fine. Of course, that would require you to have a Hashable interface for your object types instead of your Equalizer interface, so it isn't much less work than you are already doing, but you do get the cheap lookups.
How about rather doing this:
public interface Hashable {
function hash():String;
}
personally, I ask myself, why you want to do this ... hashing objects to obtain keys makes little sense if they are mutable ...
also, you might consider using a different approach, as for example this factory:
package {
public class Person {
/**
* don't use this!
* #private
*/
public function Person(name:String, age:int) {
if (!instantiationAllowed)
throw new Error("use Person.getPerson instead of constructor");
//...
}
private static var instantiationAllowed:Boolean = false;
private static var map:Object = {};
private static function create(name:String, age:int):Person {
instantiationAllowed = true;
var ret:Person = new Person(name, age);
instantiationAllowed = false;
}
public static function getPerson(name:String, age:int):Person {
var ageMap:Array = map[name];
if (ageMap == null) {
map[name] = ageMap = [];
return ageMap[age] = Person.create(name, age);
}
if (ageMap.hasOwnProperty(age))
return ageMap[age];
return ageMap[age] = Person.create(name, age);
}
}
}
it ensures, there's only one person with a given name and age (if that makes any sense) ...
Old thread I know, but still worth posting.
const jonny1 : Person = new Person("jonny", 26); const jonny2 : Person = new Person("jonny", 26);
is creating two completely different objects that will not compare using ==, guess I don't see why it's any more of a road block because of as3
The problem with AS3/JavaScript/EcmaScript is not that they create two different, equivalent objects.
The problem is that they cannot equate those two equivalent objects--only identity works, since there is no equals or hashCode methods that can be overriden with class-specific comparison logic.
For Map implementations such as dynamic Object or Dictionary, this means that you have to either use Strings or references as keys: you cannot recover objects from a map using different but equivalent objects.
To work around that problem, people either resort to strict toString implementations (for Object maps) which is undesirable, or to instance control for Dictionaries, as in #back2dos example, which introduces different problems (Also, note that #back2dos solution does not really guarantee unique Person instances since there is a time window during which asynchronous threads will be allowed to instantiate new Persons).
#A.Levy's solution is good except that in general, hashCodes are not strictly required to issue unique values (they are meant to map entries to buckets allowing for fast lookups, wherein fine-grained differentiation is done through equals method).
You need both a hashCode and an equals method, e.g.
public interface IEquable
{
function equals(object : Object) : Boolean;
function hash():String;
}
In any programming language,
const jonny1 : Person = new Person("jonny", 26);
const jonny2 : Person = new Person("jonny", 26);
is creating two completely different objects that will not compare using ==, guess I don't see why it's any more of a road block because of as3