I am studying Ceylon and have question about it metamodel. I want to create some create some base class 'DataContainer' which allow to instantiate immutable classes with build-in equals-hash implementation:
e.g. Identifier(125, "ab") == Identifier(125, "ab")
So base class should collect all shared non-variable values and use this information in 'hash' an 'equals' methods.
I have wrote this code:
shared abstract class DataContainer(ClassDeclaration declaration) {
value members = {
for (i in declaration.memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "hash", i.name != "string") i
};
variable Integer? hashCode = null;
shared actual Boolean equals(Object that) {
if (is DataContainer that) {
for (item in members) {
value thisMember = item.memberGet(this);
value thatMember = item.memberGet(that);
if (exists thisMember, exists thatMember) {
if (thisMember != thatMember) { return false; }
} else if (thisMember exists != thatMember exists) { return false; }
}
return true;
}
return false;
}
shared actual Integer hash => hashCode else (hashCode = calculateHash());
Integer calculateHash() {
variable value result = 0;
for(i in members) {
if (exists member = i.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}
}
class Identifier(shared Integer? id, shared String? name) extends DataContainer(`class`) {}
The Identifier class is the client of DataContainer. I like this solution in whole but I have to pass 'class' into the super class constructor because if I use 'class' inside DataContainer it doesn't see any members of subclass.
How can I obtain actual list of extended class's members in base class methods?
Something like 'this' doesn't work...
I found solution thanks to guys from Ceylon community. The function classDeclaration(this) from ceylon.language.meta should be used instead of 'class'.
This is the final code:
shared abstract class DataContainer() {
variable Integer? _hash = null;
variable ValueDeclaration[]? _members = null;
shared actual Boolean equals(Object that) {
if (is DataContainer that) {
for (i in members) {
value thisMember = i.memberGet(this);
value thatMember = i.memberGet(that);
if (exists thisMember, exists thatMember) {
if (thisMember != thatMember) { return false; }
} else if (thisMember exists != thatMember exists) { return false; }
}
return true;
}
return false;
}
shared actual Integer hash => _hash else (_hash = calculateHash());
ValueDeclaration[] members => _members else (_members = [
for (i in classDeclaration(this).memberDeclarations<ValueDeclaration>())
if (!i.variable, i.name != "string", i.name != "hash") i
]);
Integer calculateHash() {
variable Integer result = 0;
for (i in members) {
if (exists member = i.memberGet(this)) {
result = result.xor(member.hash);
}
}
return result;
}
}
class Identifier(shared Integer? number, shared String? name) extends DataContainer() {}
Adding to Alexander's formidable answer, I took the liberty to implement a string function as well so you can just print(myObj) and it renders nicely:
shared default actual String string {
value s = StringBuilder();
s.append(type(this).declaration.name).append(" { ");
for (i in members) {
value m = i.memberGet(this);
s.append(i.name).append(":");
s.append(if(exists m) then m.string else "<null>");
}
return s.append(" }").string;
}
Related
I think I need some feedback on my collection classes - still learning typescript and javascript and these implementations can surely be improved. I am looking forward to any suggestion. I think I do use the generic types in a useful way, any advice here would be appreciated.
The answer I am looking for most is removing the duplicate IHashTable definition from the end of both snippets and moving it to its own file, I cannot get that done it seems. I am even unsure if this IS an interface in the first place. It compiles and works this way, as far as I can see.
The collection types are incomplete and only define the basic most function at the moment. Once I am sure I use the language and its features correct the other functions should not be too difficult.
Here is my HashSet:
import { IHashable } from "./IHashable"
export class HashSet<T extends IHashable> {
private _items: HashTable<T>;
public constructor() {
this._items = {};
}
public Add(key: T): void {
let str: string = key.GetHash();
if (this._items[str] == null) {
this._items[str] = key;
}
else {
throw new RangeError("Key '" + str + "' already exists.");
}
}
public Contains(key: T): boolean {
let str: string = key.GetHash();
return this._items[str] != null;
}
}
interface HashTable<T> {
[key: string]: T;
}
I wonder if I can avoid the checking-before-adding in a way. The javascript-dictionary this relies on does allow duplicates, so to avoid them there is no other way than to check myself?
This is my Dictionary:
import { IHashable } from "./IHashable"
export class Dictionary<T1 extends IHashable, T2> {
private _items: HashTable<KeyValuePair<T1, T2>>;
public constructor() {
this._items = {};
}
public Add(key: T1, value: T2) {
let str: string = key.GetHash();
if (this._items[str] == null) {
let kvp: KeyValuePair<T1, T2> = new KeyValuePair(key, value);
this._items[str] = kvp;
}
else {
throw new RangeError("Key '" + str + "' already exists.");
}
}
public ContainsKey(key: T1): boolean {
let str: string = key.GetHash();
return this._items[str] != null;
}
public Get(key: T1): T2 {
let str: string = key.GetHash();
let kvp: KeyValuePair<T1, T2> = this._items[str];
if (kvp == null) throw new RangeError("Key '" + str + "' not found")
return kvp.Value;
}
}
export class KeyValuePair<T1 extends IHashable, T2> {
private _key: T1;
private _value: T2;
public get Key(): T1 { return this._key; }
public get Value(): T2 { return this._value; }
public constructor(key: T1, value: T2) {
this._key = key;
this._value = value;
}
}
interface HashTable<T> {
[key: string]: T;
}
Both rely on a definition of IHashable (hashABLE and hashTABLE: I should find other names.)
export interface IHashable {
GetHash(): string;
}
The dictionary looks a bit strange, it "wraps" my dictionary into a new type KeyValuePair and then uses this in the javascript dictionary. What I hope to gain by doing this is get my own type for key, in and out, as long as it offers a string by which it can be indexed. - No idea if that makes sense or is completly wrong.
What I am missing is the count of items in the collection, a way to remove items, and a way to iterate over the keys and the values.
Regarding iterating over I will post another question with my implementation of a list and a ForEach over it, hoping iterating the keys or values might be possible in the same way.
Probably the most important question I forgot here: How could the GetHash-Method be build for an own class? I was going to have a static number on my classes, and count up by 1 in the constructor before assign this number to each instance. This would guarantee uniqueness... is there something better?
Thanks for any tip!
Ralf
I am creating a method that will take a collection of different types (divs, spans..) and search it. I can't find a parameter to pass different collection types though. I tried IElementContainer and ElementCollections but I can't cast DivCollection to either. Is there another way?
The method that does the search:
private static ElementCollection searchCollections(IElementContainer ec, WACore.compStringInfo info)
{
if (info.componentIDName == WACore.componentIDs.Id.ToString())
{
return ec.Elements.Filter(Find.ById(info.componentIDValue));
}
else if (info.componentIDName == WACore.componentIDs.Name.ToString())
{
return ec.Elements.Filter(Find.ByName(info.componentIDValue));
}
else if (info.componentIDName == WACore.componentIDs.Title.ToString())
{
return ec.Elements.Filter(Find.ByTitle(info.componentIDValue));
}
else if (info.componentIDName == WACore.componentIDs.OuterText.ToString())
{
String str = info.componentIDValue.Substring(1, 6);
return ec.Elements.Filter(Find.ByText(new Regex(str)));
}
else
{
return null;
}
}
The caller(s):
return searchCollections((IElementContainer)doc.TextFields, info);
return searchCollections((IElementContainer)(doc.Divs), info);
return searchCollections((IElementContainer)doc.Spans, info);
I'm write this function:
public static String QueryString(string queryStringKey)
{
if (HttpContext.Current.Request.QueryString[queryStringKey] != null)
{
if (HttpContext.Current.Request.QueryString[queryStringKey].ToString() != string.Empty)
return HttpContext.Current.Request.QueryString.GetValues(1).ToString();
}
return "NAQ";
}
And i want to get just one value from querystring parameter.
for example i send "page" to my function and url is: "sth.com/?page=1&page=2"
and function return to me: "1,2" ; but i want first value: "1", How?
GetValues returns a string[] if the key exists. An array is zero based, so you get the first element by using array[0], you are using GetValues(1) in your code, i assume that you wanted the first.
You could also use the Enumerable.First extension method:
Request.QueryString.GetValues("page").First();
Since GetValues returns not an empty array but null if the key was not present you need to check that explicitely (FirstOrDefault doesn't work):
public static String QueryString(string queryStringKey)
{
if (HttpContext.Current != null && HttpContext.Current.Request != null)
{
string[] values = HttpContext.Current.Request.QueryString.GetValues("queryStringKey");
if (values != null) return values.First();
}
return "NAQ";
}
A better approach would be -
public static String QueryString(string queryStringKey)
{
if (HttpContext.Current!=null && HttpContext.Current.Request!=null && !string.IsNullOrEmpty(HttpContext.Current.Request.QueryString[queryStringKey])
{
return HttpContext.Current.Request.QueryString.GetValues(queryStringKey).First();
}
return "NAQ";
}
I have declared my c# application constant this way:
public class Constant
public struct profession
{
public const string STUDENT = "Student";
public const string WORKING_PROFESSIONAL = "Working Professional";
public const string OTHERS = "Others";
}
public struct gender
{
public const string MALE = "M";
public const string FEMALE = "F";
}
}
My validation function:
public static bool isWithinAllowedSelection(string strValueToCheck, object constantClass)
{
//convert object to struct
//loop thru all const in struct
//if strValueToCheck matches any of the value in struct, return true
//end of loop
//return false
}
During runtime, I will like pass in the user inputted value and the struct to check if the value exist in the struct. The struct can be profession and gender. How can I achieve it?
Example:
if(!isWithinAllowedSelection(strInput,Constant.profession)){
response.write("invalid profession");
}
if(!isWithinAllowedSelection(strInput,Constant.gender)){
response.write("invalid gender");
}
You probably want to use enums, not structs with constants.
Enums gives you a lot of possibilities, it is not so hard to use its string values to save it to the database etc.
public enum Profession
{
Student,
WorkingProfessional,
Others
}
And now:
To check existence of value in Profession by value's name:
var result = Enum.IsDefined(typeof(Profession), "Retired"));
// result == false
To get value of an enum as a string:
var result = Enum.GetName(typeof(Profession), Profession.Student));
// result == "Student"
If you really can't avoid using value names with whitespaces or other special characters, you can use DescriptionAttribute:
public enum Profession
{
Student,
[Description("Working Professional")] WorkingProfessional,
[Description("All others...")] Others
}
And now, to get description from Profession value you can use this code (implemented here as an extension method):
public static string Description(this Enum e)
{
var members = e.GetType().GetMember(e.ToString());
if (members != null && members.Length != 0)
{
var attrs = members.First()
.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length != 0)
return ((DescriptionAttribute) attrs.First()).Description;
}
return e.ToString();
}
This method fetches description defined in attribute and if there's none, returns value name. Usage:
var e = Profession.WorkingProfessional;
var result = e.Description();
// result == "Working Professional";
I want to check in my function if a passed argument of type object is empty or not. Sometimes it is empty but still not null thus I can not rely on null condition. Is there some property like 'length'/'size' for flex objects which I can use here.
Please help.
Thanks in advance.
If you mean if an Object has no properties:
var isEmpty:Boolean = true;
for (var n in obj) { isEmpty = false; break; }
This is some serious hack but you can use:
Object.prototype.isEmpty = function():Boolean {
for(var i in this)
if(i != "isEmpty")
return false
return true
}
var p = {};
trace(p.isEmpty()); // true
var p2 = {a:1}
trace(p2.isEmpty()); // false
You can also try:
ObjectUtil.getClassInfo(obj).properties.length > 0
The good thing about it is that getClassInfo gives you much more info about the object, eg. you get the names of all the properties in the object, which might come in handy.
If object containes some 'text' but as3 doesn't recognize it as a String, convert it to string and check if it's empty.
var checkObject:String = myObject;
if(checkObject == '')
{
trace('object is empty');
}
Depends on what your object is, or rather what you expect it to have. For example if your object is supposed to contain some property called name that you are looking for, you might do
if(objSomeItem == null || objSomeItem.name == null || objSomeItem.name.length == 0)
{
trace("object is empty");
}
or if your object is actually supposed to be something else, like an array you could do
var arySomeItems = objSomeItem as Array;
if(objSomeItem == null || arySomeItems == null || arySomeItems.length == 0)
{
trace("object is empty");
}
You could also use other ways through reflection, such as ObjectUtil.getClassInfo, then enumerate through the properties to check for set values.... this class help:
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
public class ReflectionUtils
{
/** Returns an Array of All Properties of the supplied object */
public static function GetVariableNames(objItem:Object):Array
{
var xmlPropsList:XMLList = describeType(objItem)..variable;
var aryVariables:Array = new Array();
if (xmlPropsList != null)
{
for (var i:int; i < xmlPropsList.length(); i++)
{
aryVariables.push(xmlPropsList[i].#name);
}
}
return aryVariables;
}
/** Returns the Strongly Typed class of the specified library item */
public static function GetClassByName($sLinkageName:String):Class
{
var tObject:Class = getDefinitionByName($sLinkageName) as Class;
return tObject;
}
/** Constructs an instance of the speicified library item */
public static function ConstructClassByName($sLinkageName:String):Object
{
var tObject:Class = GetClassByName($sLinkageName);
//trace("Found Class: " + tMCDefinition);
var objItem:* = new tObject();
return objItem;
}
public static function DumpObject(sItemName:String, objItem:Object):void
{
trace("*********** Object Dump: " + sItemName + " ***************");
for (var sKey:String in objItem)
{
trace(" " + sKey +": " + objItem[sKey]);
}
}
//}
}
Another thing to note is you can use a simple for loop to check through an objects properties, thats what this dumpobject function is doing.
You can directly check it as follow,
var obj:Object = new Object();
if(obj == null)
{
//Do something
}
I stole this from a similar question relating to JS. It requires FP 11+ or a JSON.as library.
function isEmptyObject(obj){
return JSON.stringify(obj) === '{}';
}
can use use the hasProperty method to check for length
var i:int = myObject.hasProperty("length") ? myObject.length: 0;