ES6 read-only enums that can map value to name - enums

I would like to define an enum-like structure in JS, but have two requirements:
The values be read-only, i.e. no users can assign to them.
The values (0, 1, 2, ...) can be mapped back into the names (as with Java's name method)
The methods I know to create enums like this typically meet one requirement or the other, not both.
I've tried:
const MyEnum = {
a: 0,
b: 1,
c: 2
};
The enum itself is constant, but the values are still mutable and I can't map values back to names efficiently.
When writing an enum in Typescript, it outputs:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["a"] = 0] = "a";
MyEnum[MyEnum["b"] = 1] = "b";
MyEnum[MyEnum["c"] = 2] = "c";
})(MyEnum || (MyEnum = {}));
This can map both ways, but still doesn't have constant values.
The only option I've found that meets both requirements would be using getters on a class:
class MyEnum {
get a() {
return 0;
}
...
}
This method dramatically restricts the legal names and has a lot of overhead, especially in browsers that don't inline getters well (or can't).
#Shmiddty suggested freezing an object:
const MyEnum = Object.freeze({
a: 0,
b: 1,
c: 2
});
This meets the constant requirement well, but doesn't provide a great way to map values back to names.
I could write a helper that builds the reverse mapping like:
function reverseEnum(enum) {
Object.keys(enum).forEach(k => {
enum[enum[k]] = k;
});
}
But any kind of programmatic solution to generate the reverse mapping will run into problems if the original object is frozen or otherwise actually constant.
Is there a clean, concise solution to this in JS?

This does a pretty good job, IMHO.
function Enum(a){
let i = Object
.keys(a)
.reduce((o,k)=>(o[a[k]]=k,o),{});
return Object.freeze(
Object.keys(a).reduce(
(o,k)=>(o[k]=a[k],o), v=>i[v]
)
);
} // y u so terse?
const FOO = Enum({
a: 0,
b: 1,
c: "banana"
});
console.log(FOO.a, FOO.b, FOO.c); // 0 1 banana
console.log(FOO(0), FOO(1), FOO("banana")); // a b c
try {
FOO.a = "nope";
}
catch (e){
console.log(e);
}

I'd use a Map so that your enum values can be any type, rather than having them coerced into strings.
function Enum(obj){
const keysByValue = new Map();
const EnumLookup = value => keysByValue.get(value);
for (const key of Object.keys(obj)){
EnumLookup[key] = obj[key];
keysByValue.set(EnumLookup[key], key);
}
// Return a function with all your enum properties attached.
// Calling the function with the value will return the key.
return Object.freeze(EnumLookup);
}
If your enum is all strings, I'd also probably change one line to:
EnumLookup[key] = Symbol(obj[key]);
to ensure that the enum values are being used properly. Using just a string, you have no guarantee that some code hasn't simply passed a normal string that happens to be the same as one of your enum values. If your values are always strings or symbols, you could also swap out the Map for a simple object.

Just recently implemented an Es6 version that works quite well:
const k_VALUES = {}
export class ErrorCode {
constructor(p_apiCode, p_httpCode){
this.apiCode = p_apiCode;
this.httpCode = p_httpCode;
k_VALUES[p_apiCode] = this;
}
static create(p_apiCode){
if(k_VALUES[p_apiCode]){
return k_VALUES[p_apiCode];
}
return ErrorCode.UNKNOWN;
}
}
ErrorCode.UNKNOWN = new ErrorCode(0, 500);
ErrorCode.NOT_FOUND = new ErrorCode(-1000, 404);
ErrorCode.NOT_FOUND_EMAIL = new ErrorCode(-1001, 404);
ErrorCode.BAD_REQUEST = new ErrorCode(-1010, 404);
I wanted to implement a similar pattern as what we do with Java enums. This enables me to use a constructor to pass values. The constructor then freezes the ErrorCode object - nice and convenient.
Usage: first import your enum class...
import {ErrorCode} from "../common/services/errors/ErrorCode";
Now, after importing the enum class, access it like so:
if( errCode.includes(ErrorCode.BAD_REQUEST.apiCode) ){...}
PS> This is used in conjunction with a Webpack setup using Babel to convert our ES6 classes down for browser compatibility.

Related

LINQ Distinct does not invoke IEquatable<T>.Equals

I have a set of domain object, deriving from a base, where I've overridden Equals, IEquatable<T>.Equals and equality operators. I've successfully used Contains, but now I am trying to use Distinct differently. Here's look at a sample code:
var a = new Test { Id = 1 };
var a2 = new Test { Id = 1 };
var list = new List<Test> { a, a2 };
var distinct = list.Distinct().ToList(); // both objects, Equal implementations not called
var containsA = list.Contains(a); // true, Equal implementations called
var containsA2 = list.Contains(a); // true
var containsNewObjectWithSameId = list.Contains(new Test { Id = 1 }); // true
public class Test : IEquatable<Test>
{
public int Id { get; init; }
public bool Equals(Test other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
if (this.GetType() != other.GetType())
return false;
return this.Id == other.Id;
}
public override int GetHashCode() => base.GetHashCode + this.Id;
}
Contains finds matches, but Distinct is feeling very inclusive and keeps them both. From MS docs:
The first search does not specify any equality comparer, which means FindFirst uses
EqualityComparer.Default to determine equality of boxes. That in turn uses the implementation
of the IEquatable.Equals method in the Box class.
What am I missing?
Thanks #JonSkeet for your insight in the comments.
The problem in this case is the way I wrote my GetHashCode method. It has nothing to do with LINQ, as I originally thought.
Explanation
GetHashCode has to be identical for objects that compare equally. In my case - since the base implementation of object.Equals only checks for reference equality and I am comparing two separate objects - a and b, their base.GetHashCode would result in different values, which in turn would render those two objects as not equal.
Solution
In this case, simply returning the Id value is enough as is shown in MS docs:
One of the simplest ways to compute a hash code for a numeric value that has the same or a smaller range than the Int32 type is to simply return that value.
So changing the above code sample like this:
public override int GetHashCode() => this.Id;
would solve the issue. Please keep in mind that if the value of Id is not unique, this will cause ill behavior. In such cases you'll need another property to check and you will have to compose GetHashCode from ALL those properties. For further info refer to MS docs

How to get multiple properties from objects in JXA?

Is there a way in JXA to get multiple properties from multiple objects with a single call?
For example, I want to get name and enabled property from menu items which can be done for each individual property as follows:
Application("System Events").processes.byName('Finder').menuBars[0].menuBarItems.name()
Application("System Events").processes.byName('Finder').menuBars[0].menuBarItems.enabled()
but is it possible to get them with a single function call? Something like:
Application("System Events").processes.byName('Finder').menuBars[0].menuBarItems.select('name', 'enabled')
I know, that I can iterate through the menuBarItems and collect properties from .properties() method, but this approach is too slow, that's why I'm looking for other options.
UPDATE
I'm looking for better performance, not for nicer syntax, i.e. I want properties to be retrieved in a single call to System Events.
I'd probably do it like this:
sys = Application('com.apple.systemevents');
FinderProc = sys.processes['Finder'];
FinderMenuBarItems = FinderProc.menuBars[0].menuBarItems();
Array.from(FinderMenuBarItems,x=>[x.name(),x.enabled()]);
By first converting the object to an array, this allows one to map each element and retrieve the desired properties for all in one go. The code is split over several lines for ease of reading.
EDIT: added on 2019-07-27
Following on from your comment regarding Objective-C implementation, I had a bit of time today to write a JSObjc script. It does the same thing as the vanilla JXA version above, and, yes, it clearly makes multiple function calls, which is necessary. But it's performing these functions at a lower level than System Events (which isn't involved at all here), so hopefully you'll find it more performant.
ObjC.import('ApplicationServices');
ObjC.import('CoreFoundation');
ObjC.import('Foundation');
ObjC.import('AppKit');
var err = {
'-25211':'APIDisabled',
'-25206':'ActionUnsupported',
'-25205':'AttributeUnsupported',
'-25204':'CannotComplete',
'-25200':'Failure',
'-25201':'IllegalArgument',
'-25202':'InvalidUIElement',
'-25203':'InvalidUIElementObserver',
'-25212':'NoValue',
'-25214':'NotEnoughPrecision',
'-25208':'NotImplemented',
'-25209':'NotificationAlreadyRegistered',
'-25210':'NotificationNotRegistered',
'-25207':'NotificationUnsupported',
'-25213':'ParameterizedAttributeUnsupported',
'0':'Success'
};
var unwrap = ObjC.deepUnwrap.bind(ObjC);
var bind = ObjC.bindFunction.bind(ObjC);
bind('CFMakeCollectable', [ 'id', [ 'void *' ] ]);
Ref.prototype.nsObject = function() {
return unwrap($.CFMakeCollectable(this[0]));
}
function getAttrValue(AXUIElement, AXAttrName) {
var e;
var _AXAttrValue = Ref();
e = $.AXUIElementCopyAttributeValue(AXUIElement,
AXAttrName,
_AXAttrValue);
if (err[e]!='Success') return err[e];
return _AXAttrValue.nsObject();
}
function getAttrValues(AXUIElement, AXAttrNames){
var e;
var _AXAttrValues = Ref();
e = $.AXUIElementCopyMultipleAttributeValues(AXUIElement,
AXAttrNames,
0,
_AXAttrValues);
if (err[e]!='Success') return err[e];
return _AXAttrValues.nsObject();
}
function getAttrNames(AXUIElement) {
var e;
var _AXAttrNames = Ref();
e = $.AXUIElementCopyAttributeNames(AXUIElement, _AXAttrNames);
if (err[e]!='Success') return err[e];
return _AXAttrNames.nsObject();
}
(() => {
const pid_1 = $.NSWorkspace.sharedWorkspace
.frontmostApplication
.processIdentifier;
const appElement = $.AXUIElementCreateApplication(pid_1);
const menuBar = getAttrValue(appElement,"AXMenuBar");
const menuBarItems = getAttrValue(menuBar, "AXChildren");
return menuBarItems.map(x => {
return getAttrValues(x, ["AXTitle", "AXEnabled"]);
});
})();

Is there a Dataflow TransformBlock that receives two input arguments?

I have a delegate that takes two numbers and creates a System.Windows.Point from them:
(x, y) => new Point(x,y);
I want to learn how can I use TPL Dataflow, specifically TransformBlock, to perform that.
I would have something like this:
ISourceBlock<double> Xsource;
ISourceBlock<double> Ysource;
ITargetBlock<Point> PointTarget;
// is there such a thing?
TransformBlock<double, double, Point> PointCreatorBlock;
// and also, how should I wire them together?
UPDATE:
Also, how can I assemble a network that joins more than two arguments? For example, let's say I have a method that receives eight arguments, each one coming from a different buffer, how can I create a block that knows when every argument has one instance available so that the object can be created?
I think what your looking for is the join block. Currently there is a two input and a three input variant, each outputs a tuple. These could be combined to create an eight parameter result. Another method would be creating a class to hold the parameters and using various block to process and construct the parameters class.
For the simple example of combining two ints for a point:
class MyClass {
BufferBlock<int> Xsource;
BufferBlock<int> Ysource;
JoinBlock<int, int> pointValueSource;
TransformBlock<Tuple<int, int>, Point> pointProducer;
public MyClass() {
CreatePipeline();
LinkPipeline();
}
private void CreatePipeline() {
Xsource = new BufferBlock<int>();
Ysource = new BufferBlock<int>();
pointValueSource = new JoinBlock<int, int>(new GroupingDataflowBlockOptions() {
Greedy = false
});
pointProducer = new TransformBlock<Tuple<int, int>, Point>((Func<Tuple<int,int>,Point>)ProducePoint,
new ExecutionDataflowBlockOptions()
{ MaxDegreeOfParallelism = Environment.ProcessorCount });
}
private void LinkPipeline() {
Xsource.LinkTo(pointValueSource.Target1, new DataflowLinkOptions() {
PropagateCompletion = true
});
Ysource.LinkTo(pointValueSource.Target2, new DataflowLinkOptions() {
PropagateCompletion = true
});
pointValueSource.LinkTo(pointProducer, new DataflowLinkOptions() {
PropagateCompletion = true
});
//pointProduce.LinkTo(Next Step In processing)
}
private Point ProducePoint(Tuple<int, int> XandY) {
return new Point(XandY.Item1, XandY.Item2);
}
}
The JoinBlock will wait until it has data available on both of its input buffers to produce an output. Also, note that in this case if X's and Y's are arriving out of order at the input buffers care needs to be taken to re-sync them. The join block will only combine the first X and the first Y value it receives and so on.

Enum values as parameter default values in Haxe

Is there a way to use enum default parameters in Haxe? I get this error:
Parameter default value should be constant
enum AnEnum {
A;
B;
C;
}
class Test {
static function main() {
Test.enumNotWorking();
}
static function enumNotWorking(e:AnEnum = AnEnum.A){}
}
Try Haxe link.
Update: this feature has been added in Haxe 4. The code example from the question now compiles as-is with a regular enum.
Previously, this was only possible if you're willing to use enum abstracts (enums at compile time, but a different type at runtime):
#:enum
abstract AnEnum(Int)
{
var A = 1;
var B = 2;
var C = 3;
}
class Test3
{
static function main()
{
nowItWorks();
}
static function nowItWorks(param = AnEnum.A)
{
trace(param);
}
}
There's nothing special about the values I chose, and you could choose another type (string, or a more complex type) if it better suits your use case. You can treat these just like regular enums (for switch statements, etc.) but note that when you trace it at runtime, you'll get "1", not "A".
More information: http://haxe.org/manual/types-abstract-enum.html
Sadly enums can't be used as default values, because in Haxe enums aren't always constant.
This piece of trivia was on the old website but apparently hasn't made it into the new manual yet:
http://old.haxe.org/ref/enums#using-enums-as-default-value-for-parameters
The workaround is to check for a null value at the start of your function:
static function enumNotWorking(?e:AnEnum){
if (e==null) e=AnEnum.A;
}
Alternatively, an Enum Abstract might work for your case.

Create additional D3.js symbols

D3 already features a bunch of symbols, but I'd like to add a custom one. So that I could for example just call d3.svg.symbol().type('custom') in my code.
This cannot be done directly since the array of symbol definitions is not accessible from the API.
You can see in the source code HERE that the symbol definitions are stored in a d3.map called d3_svg_symbols. The only part of this map that gets exposed to the public API is the array of keys. This is done by calling the .keys() method of the map, HERE.
d3.svg.symbolTypes = d3_svg_symbols.keys();
The definitions themselves are never exposed, and so you cannot add definitions directly as you had hoped.
You can, however, construct a workaround without too much difficulty. One way would be to create a map of your custom symbols, and create a function based on the existing one for the built-in symbols. For example:
// DEFINE A COUPLE OF CUSTOM SYMBOLS
var customSymbolTypes = d3.map({
'custom-symbol-1': function(size) {
// returns a path-data string
},
'custom-symbol-2': function(size) {
// returns a path-data string
}
});
// CREATE A CUSTOM SYMBOL FUNCTION MIRRORING THE BUILT-IN FUNCTIONALITY
d3.svg.customSymbol = function() {
var type,
size = 64; // SET DEFAULT SIZE
function symbol(d,i) {
// GET THE SYMBOL FROM THE CUSTOM MAP
return customSymbolTypes.get(type.call(this,d,i))(size.call(this,d,i));
}
// DEFINE GETTER/SETTER FUNCTIONS FOR SIZE AND TYPE
symbol.type = function(_) {
if (!arguments.length) return type;
type = d3.functor(_);
return symbol;
};
symbol.size = function(_) {
if (!arguments.length) return size;
size = d3.functor(_);
return symbol;
};
return symbol;
};
Then, you could create a function to check if a symbol is in the list of built-in symbols, and if it's not, assume it is a custom symbol:
function getSymbol(type, size) {
// SIZE DEFAULTS TO 64 IF NOT GIVEN
size = size || 64;
// IF TYPE IS ONE OF THE BUILT-IN TYPES, CALL THE BUILT-IN FUNCTION
if (d3.svg.symbolTypes.indexOf(type) !== -1) {
return d3.svg.symbol().type(type).size(size)();
}
// OTHERWISE, CALL THE CUSTOM SYMBOL FUNCTION
else {
return d3.svg.customSymbol().type(type).size(size)();
}
}
HERE is a demo of this method in action.
I'll admit it seems kind of crazy to have to re-implement the whole symbol function like that. It might be worth a feature request on the github page asking to be able to provide a custom map of symbol definitions that could be read into the built-in method. Anyway, I hope that helps for now.

Resources