Using objects as Map keys in Haxe - haxe

I'm trying to make a Map with an object as a key. The problem is, that when I try to get elements from this map, I always get null. It's because I'm not providing the exact same reference as the key was. I'm providing an object with the same values, so the reference is different.
Is there any way to solve that? Can I make it use some kind of equals() function?
class PointInt
{
public var x:Int;
public var y:Int;
...
}
var map = new Map<PointInt, Hex>();
var a = new PointInt(1, 1);
var b = new PointInt(1, 1);
var hex_a = new Hex();
map[a] = hex_a;
var hex_b = map[b];
/// hex_b == null now because reference(a) == reference(b)

As explained here and here, Map in Haxe works using the reference of the object as the key.
What you want to use instead is a HashMap like this (try.haxe link):
import haxe.ds.HashMap;
class Test {
static function main() {
var map = new HashMap();
map.set(new PointInt(1, 1), 1);
trace(map.get(new PointInt(1,1)));
}
}
class PointInt
{
public var x:Int;
public var y:Int;
public function new(x:Int, y:Int)
{
this.x = x;
this.y = y;
}
public function hashCode():Int
{
return x + 1000*y; //of course don't use this, but a real hashing function
}
public function toString()
{
return '($x,$y)';
}
}
What you need to change in your code, besides using haxe.ds.HashMap instead of Map is to implement a hashCode : Void->Int function in your key object
Since you're using an object that has 2 ints, and the hash map is just 1 int, it will happen that 2 PointInt will have the same hash code. To solve this you could create a hash map that uses strings as hashcode but if you can write (or google) a good hash function you will get better performance.

Related

Generic Template String like in Python in Dart

In python, I often use strings as templates, e.g.
templateUrl = '{host}/api/v3/{container}/{resourceid}'
params = {'host': 'www.api.com', 'container': 'books', 'resourceid': 10}
api.get(templateUrl.format(**params))
This allows for easy base class setup and the like. How can I do the same in dart?
I'm assuming I will need to create a utility function to parse the template and substitute manually but really hoping there is something ready to use.
Perhaps a TemplateString class with a format method that takes a Map of name/value pairs to substitute into the string.
Note: the objective is to have a generic "format" or "interpolation" function that doesn't need to know in advance what tags or names will exist in the template.
Further clarification: the templates themselves are not resolved when they are set up. Specifically, the template is defined in one place in the code and then used in many other places.
Dart does not have a generic template string functionality that would allow you to insert values into your template at runtime.
Dart only allows you to interpolate strings with variables using the $ syntax in strings, e.g. var string = '$domain/api/v3/${actions.get}'. You would need to have all the variables defined in your code beforehand.
However, you can easily create your own implementation.
Implementation
You pretty much explained how to do it in your question yourself: you pass a map and use it to have generic access to the parameters using the [] operator.
To convert the template string into something that is easy to access, I would simply create another List containing fixed components, like /api/v3/ and another Map that holds generic components with their name and their position in the template string.
class TemplateString {
final List<String> fixedComponents;
final Map<int, String> genericComponents;
int totalComponents;
TemplateString(String template)
: fixedComponents = <String>[],
genericComponents = <int, String>{},
totalComponents = 0 {
final List<String> components = template.split('{');
for (String component in components) {
if (component == '') continue; // If the template starts with "{", skip the first element.
final split = component.split('}');
if (split.length != 1) {
// The condition allows for template strings without parameters.
genericComponents[totalComponents] = split.first;
totalComponents++;
}
if (split.last != '') {
fixedComponents.add(split.last);
totalComponents++;
}
}
}
String format(Map<String, dynamic> params) {
String result = '';
int fixedComponent = 0;
for (int i = 0; i < totalComponents; i++) {
if (genericComponents.containsKey(i)) {
result += '${params[genericComponents[i]]}';
continue;
}
result += fixedComponents[fixedComponent++];
}
return result;
}
}
Here would be an example usage, I hope that the result is what you expected:
main() {
final templateUrl = TemplateString('{host}/api/v3/{container}/{resourceid}');
final params = <String, dynamic>{'host': 'www.api.com', 'container': 'books', 'resourceid': 10};
print(templateUrl.format(params)); // www.api.com/api/v3/books/10
}
Here it is as a Gist.
Here is my solution:
extension StringFormating on String {
String format(List<String> values) {
int index = 0;
return replaceAllMapped(new RegExp(r'{.*?}'), (_) {
final value = values[index];
index++;
return value;
});
}
String formatWithMap(Map<String, String> mappedValues) {
return replaceAllMapped(new RegExp(r'{(.*?)}'), (match) {
final mapped = mappedValues[match[1]];
if (mapped == null)
throw ArgumentError(
'$mappedValues does not contain the key "${match[1]}"');
return mapped;
});
}
}
This gives you a very similar functionality to what python offers:
"Test {} with {}!".format(["it", "foo"]);
"Test {a} with {b}!".formatWithMap({"a": "it", "b": "foo"})
both return "Test it with foo!"
It's even more easy in Dart. Sample code below :
String host = "www.api.com"
String container = "books"
int resourceId = 10
String templateUrl = "$host/api/v3/$container/${resourceId.toString()}"
With the map, you can do as follows :
Map<String, String> params = {'host': 'www.api.com', 'container': 'books', 'resourceid': 10}
String templateUrl = "${params['host']}/api/v3/${params['container']}/${params['resourceId']}"
Note : The above code defines Map as <String, String>. You might want <String, Dynamic> (and use .toString())
Wouldn't it be simplest to just make it a function with named arguments? You could add some input validation if you wanted to.
String templateUrl({String host = "", String container = "", int resourceid = 0 }) {
return "$host/api/v3/$container/$resourceId";
}
void main() {
api.get(templateUrl(host:"www.api.com", container:"books", resourceid:10));
}

Node + ES6 classes: Setting up a set of cached objects

I've tried to search for instance caching and singletons on Google and StackOverflow without success, seeing only posts about module.exports, if you know a post that answers this question, feel free to reference it. Thank you!
I have an application that needs to work on a set of objects that rarely change, and hence need to be cached for performance optimisation.
Here is a toy example where a single property is set directly.
When I call the application, I export an object that will contain the set of cached objects in assets_cached.js:
const Assets = {};
module.exports.Assets = Assets;
In another module of the application I have an ES6 class:
const _ = require('lodash')
const { Assets } = require('./assets_cached')
class Asset {
constructor(id, some_property) {
if (id in Assets) {
// Update instance data with cached properties
_.assign(this, Assets_cached[id]);
} else {
// If it's not cached, create a new object
this.id = id;
this.some_property = some_property;
// Cache this object
Assets_cached[id] = this;
}
}
getProperty() {
return this.some_property;
}
setProperty(value) {
this.some_property = value;
// Is there a way of avoiding having to do this double assignment?
Assets_cached[id].some_property = value;
}
}
module.exports = Asset;
How may I avoid having to set the some_property twice (in the current instance and the cache, while ensuring that other instances are updated in parallel)?
Ideally I'd like to do something like:
if (id in Assets) {
this = Assets.cached[id]
}
inside the constructor, but this is not possible.
What's the most elegant and correct way of making this work?
Ideally I'd like to do something like this = Assets.cached[id] inside the constructor
The magic keyword here is return. You can just return an arbitrary object from the constructor and it will be used instead of this.
constructor(id, some_property) {
if (id in Assets) {
// use cached instance instead of creating a new one
return Assets_cached[id];
} else {
this.id = id;
this.some_property = some_property;
// Cache this object
Assets_cached[id] = this;
}
}
Here is the approach to the comment that was made some half an hour ago ...
const { Assets_cached } = require('./assets_cached');
// const { AssetStore } = require('./assetstore');
class Asset {
constructor(id, some_property) { // clean/lean constructor.
this.id = id;
this.some_property = some_property;
}
getProperty() {
return this.some_property;
}
setProperty(value) {
this.some_property = value;
}
}
function isAsset(type) {
// poor man's approach ... change to something more feasible.
return (type instanceof Asset);
}
function createAsset(id, some_property) { // factory that also handles caching.
var
asset = Assets_cached[id];
// asset = AssetStore.get(id);
if (!(asset && isAsset(asset))) {
asset = Assets_cached[id] = (new Asset(id, some_property));
// AssetStore.put(id, (asset = new Asset(id, some_property)));
}
return asset;
}
module.exports = {
create : createAsset,
isAsset : isAsset
};
Note
One also should consider providing a minimal API to Assets_cached, something like put/set, get and delete instead of Assets_cached being an entirely exposed, plain key-value store.

Java util hashmap containsKey()

I have been having some trouble using the function containsKey. I wrote a small program to show where I am expecting containsKey to give me a different result:
HashMap<IdentifierInterface, Set<NaturalNumberInterface>> hashMap;
HashMap<StringBuffer, Integer> works;
TryHashmap(){
hashMap = new HashMap<IdentifierInterface, Set<NaturalNumberInterface>>();
works = new HashMap<StringBuffer, Integer>();
}
private void start() {
Identifier iden = new Identifier('a');
NaturalNumber nn = new NaturalNumber('8');
Set<NaturalNumberInterface> set = new Set<NaturalNumberInterface>();
set.insert(nn);
hashMap.put(iden, set);
System.out.println(hashMap.containsKey(iden));
Identifier newIden = new Identifier('a');
System.out.println(hashMap.containsKey(newIden)); //TODO why is this not true?
iden.init('g');
System.out.println(hashMap.containsKey(iden));
}
public static void main(String[] argv) {
new TryHashmap().start();
}
The constructor of the Identifier class is as follows, the init() is similar but it will remove anything that was in the identifier before.
Identifier(char c){
iden = new StringBuffer();
iden.append(c);
}
I put something into the hashmap using an Identifier as key, but when I try to use an Identifier with a different name but with the same content the containsKey function returns false where I am expecting a true.
(the output prints true
false
true)
Thanks in advance!
Implement equals() and hashCode() for the identifier object. hashCode is needed to find the relevant bucket and equals is required to handle collisions while hashing.
Further Reading
method containsKey in HashMap.class
/**
* Returns <tt>true</tt> if this map contains a mapping for the
* specified key.
*
* #param key The key whose presence in this map is to be tested
* #return <tt>true</tt> if this map contains a mapping for the specified
* key.
*/
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
method getEntry in HashMap.class
/**
* Returns the entry associated with the specified key in the
* HashMap. Returns null if the HashMap contains no mapping
* for the key.
*/
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
the method getEntry told us that the result will be true, only if the Object a has the same hashCode() as the Object b and a.equals(b)

Random string in AS3

I have recently worked on AS3 project with a module that works like this:
I have 50 strings and I am picking one randomly of them at a given time. When I am done with the picked one I choose another one of the 49 left again randomly and so on.
I managed to solve this problem using helper arrays, for cycles, mapping index numbers with the strings. Although every thing works just fine I found my code very messed and hard to understand.
Is there a much more easy and cleaner way to solve this problem in AS3?
Maybe there is a library for getting random string out of strings?
Something simple like this class:
public class StringList
{
private var _items:Array = [];
public function StringList(items:Array)
{
_items = items.slice();
}
public function get random():String
{
var index:int = Math.random() * _items.length;
return _items.splice(index, 1);
}
public function get remaining():int{ return _items.length; }
}
And its usage:
var list:StringList = new StringList(['a', 'b', 'c', 'd']);
while(list.remaining > 0)
{
trace(list.random);
}
I'm not sure what do you want to do with that procedure, but here's one proposal:
var stringArray:Array = new Array("string1", "string2", "string2"); //your array with strings
var xlen:uint = stringArray.length-1; //we get number of iterations
for (var x:int = xlen; x >= 0; x--){ //we iterate backwards
var randomKey:Number = Math.floor(Math.random()*stringArray.length); //gives you whole numbers from 0 to (number of items in array - 1)
stringArray.splice(randomKey,1); //remove item from array with randomKey index key
var str:String = stringArray[randomKey]; //output item into new string variable or do whatever
}

Anonymous type and getting values out side of method scope

I am building an asp.net site in .net framework 4.0, and I am stuck at the method that supposed to call a .cs class and get the query result back here is my method call and method
1: method call form aspx.cs page:
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
2: Method in helper class:
public IQueryable<VariablesForIQueryble> GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some connection_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where (gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)
select new VariablesForIQueryble(m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//select new {m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap};
return query ;
}
I tried the above code with IEnumerable too without any luck. This is the code for class VariablesForIQueryble:
3:Class it self for taking anonymouse type and cast it to proper types:
public class VariablesForIQueryble
{
private int _emailCap;
public int EmailCap
{
get { return _emailCap; }
set { _emailCap = value; }
}`....................................
4: and a constructor:
public VariablesForIQueryble(int memberID, string memberFirst, string memberLast, string memberEmail, int? validEmail, int? emailCap)
{
this.EmailCap = (int) emailCap;
.........................
}
I can't seem to get the query result back, first it told me anonymous type problem, I made a class after reading this: link text; and now it tells me constructors with parameters not supported. Now I am an intermediate developer, is there an easy solution to this or do I have to take my query back to the .aspx.cs page.
If you want to project to a specific type .NET type like this you will need to force the query to actually happen using either .AsEnumerable() or .ToList() and then use .Select() against linq to objects.
You could leave your original anonymous type in to specify what you want back from the database, then call .ToList() on it and then .Select(...) to reproject.
You can also clean up your code somewhat by using an Entity Association between Groups and Members using a FK association in the database. Then the query becomes a much simpler:
var result = ctx.Members11.Include("Group").Where(m => m.Group.groupID == incomingGroupID && m.EmailCap == incomingEmailCap);
You still have the issue of having to do a select to specify which columns to return and then calling .ToList() to force execution before reprojecting to your new type.
Another alternative is to create a view in your database and import that as an Entity into the Entity Designer.
Used reflection to solve the problem:
A: Query, not using custom made "VariablesForIQueryble" class any more:
//Method in helper class
public IEnumerable GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where ((gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)) //select m;
select new { m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap };
//select new VariablesForIQueryble (m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//List<object> lst = new List<object>();
//foreach (var i in query)
//{
// lst.Add(i.MemberEmail);
//}
//return lst;
//return query.Select(x => new{x.MemberEmail,x.MemberID,x.ValidEmail,x.MemberFirst,x.MemberLast}).ToList();
return query;
}
B:Code to catch objects and conversion of those objects using reflection
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
if (query != null)
{
foreach (var objRow in query)
{
System.Type type = objRow.GetType();
int memberId = (int)type.GetProperty("MemberID").GetValue(objRow, null);
string memberEmail = (string)type.GetProperty("MemberEmail").GetValue(objRow, null);
}
else
{
something else....
}

Resources