Renaming a parent class property in subclass - javascript

In es2015, if I have base class to represent a List that looks like this:
class List {
constructor(data){
this.data = data
}
sortBy(attribute){
return this.data.sort((a,b) => {
return (a[attribute] < b[attribute]) ? 1 : -1;
})
}
get count() { return this.data.length }
}
Then I might want to subclass that base class with a less generic kind of data, namely, if I am an elf, toys:
class ToyList extends List {
constructor(toys){
super(toys);
this.toys = toys;
}
}
At this point ToyList is no different from List, except for the name. But if you look at an instantiation of ToyList, it has both data and toys properties. These refer to the same array, in terms of conceptualizing the point of a ToyList, data doesn’t make much sense.
If I make a ToyList, I have both .data and a .toys attributes:
tl = new ToyList(['truck', 'plane', 'doll'])
Object { data: Array[3], toys: Array[3] }
Then my tl has both a data and a toys attribute. They’re both references to the same array, but what I would like is for the subclass to only have the toys reference.
Here’s another example which utilizes a method on the base class:
class Todos extends List {
constructor(todos){
super(todos);
this.todos = todos;
}
get byPriority(){
return this.todos.sortBy('priority')
}
}
var thingsToDo = [
{task: 'wash the dog', priority: 10},
{task: 'do taxes', priority: 1},
{task: 'clean the garage', priority: 0}
]
var todos = new Todos(thingsToDo);
todos.byPriority
This would be nice, because then I could just refer to .byPriority to get a sorted version of the list which is very specific to this particular kind of data. But I can’t see how I can make that happen, because
But what I get is:
TypeError: this.todos.sortBy is not a function
So to summarize, what I want is a way to refer to to base class properties with a name which is specific to the semantics of the subclass, without losing the methodology of the base class.

referencing our discurrion in the comments, a better implementation (imo), extensible and avoiding the problem you asked about
var AP = Array.prototype; //just lazy
class List {
constructor(elements){
for(var i = 0, j = (elements && elements.length)|0; i<j; ++i)
this[i] = elements[i];
//use length instead of count, stay compatible with the Array-methods
//will make your life easier
this.length = i;
}
length: 0
sortBy(attr){
return this.sort(function(a,b){
return (a[attribute] < b[attribute]) ? 1 : -1
});
}
//some functions have to be wrapped, to produce a List of the right type
filter(fn){
return new (this.constructor)(AP.filter.call(this, fn));
}
clone(){ return new (this.constructor)(this) }
}
//some functions can simply be copied from Array
//no need to re-implement or even wrap them.
List.prototype.sort = AP.sort;
List.prototype.push = AP.push;
List.prototype.pop = AP.pop;
the subclass
class ToyList extends List {
constructor(toys){
//maybe you want to filter the input, before you pass it to the list
//or convert it, or whatever, it's all up to you
super(toys && AP.filter.call(toys, v=>v instanceof Toy));
}
//... additional functionality
}
and an example usage
class Toy {
constructor(name){
this.name = name;
}
}
var a = new ToyList([
new Toy("foo"),
new Toy("bar"),
"not a toy",
new Toy("baz")
])
console.log(a instanceof ToyList, a);
var b = a.filter(toy => toy.name.charAt(0) === "b");
console.log(b instanceof ToyList, b);
Edit: added your Example with the Todos
class Todos extends List {
//don't even need a constructor, since I simply want to pass
//the array to the parent-constructor
//don't use getter for functionality, use methods!
byPriority(){
return this.sortBy('priority');
}
}
var thingsToDo = [
{task: 'wash the dog', priority: 10},
{task: 'do taxes', priority: 1},
{task: 'clean the garage', priority: 0}
]
var todos = new Todos(thingsToDo);
todos.byPriority()

ToyList has both data and toys properties. These refer to the same array, in terms of conceptualizing the point of a ToyList, data doesn’t make much sense.
There's your actual problem: your ToyList doesn't make sense as a subclass of List.
If (for any reasons) your class should be similar to List, but not have a data property, then it's not a subclass any more. It would violate the Liskov substitution principle.
Now what are your options?
as you already considered, you can make it a subclass in which the more specific .toys property is an alias for .data. This is perfectly fine, but you can't avoid having that data property there as well.
you might want to outiright scrap that data property and store elements directly on the object. Your List class looks like "Array but with useful helper functions". If that was your intention, you should consider making it an actual subclass of Array. #Thomas's answer goes in that direction.
you might want to favor composition over inheritance. You've already used the concept - your List instances contain Arrays in their data properties. If you have a Wishlist or Toylist or whatever, that deal specifically with whishes or toys and have corresponding methods for them, you can simply store a List instance in their .toys slot.
You actually seemed to expect your TodoList to work like that, given the invocation of this.todos.sortBy('priority') (where this.todos would be a List). On an subclass, just this.sortBy('priority') would do the job.
I didn't really get how your ToyList is a specialisation of List. If there is nothing special about it but the name, maybe you actually don't need a different class alltogether. If JavaScript had generics or type variables, you'd use a List<Toy>, but it doesn't so you can just use Lists directly.

I think you have a lot of different problems.
Your list has a problem with the definition of sortBy, you need to take 3 cases in account, like this:
class List {
constructor(data){ this.data = data; }
sortBy(attribute){
console.log("sortBy");
return this.data.sort( (a,b) => {
if (a[attribute] < b[attribute]) return -1;
if (a[attribute] > b[attribute]) return 1;
return 0;
});
}
get count() { return this.data.length; }
}
Now you can extend the List, and if you want to name data as toys then define a get method named toys() to return the data. It may strange to you, but if you subclass List then you should use data (if not, don't subclass it). There is an alternative: you can delete data attribute and then create toys but alas, designing a sortBy method in List would be difficult (or use a second parameter to name the array to sort?). So, let's use the first suggestion:
class ToyList extends List {
constructor(toys){ super(toys); }
get toys() { return this.data; }
}
Let do the same for Todos:
class Todos extends List {
constructor(todos){ super(todos); }
get todos() { return data; }
get byPriority(){
return this.sortBy('priority');
}
}
The definition of byPriority is a little bit weird as it has a border effect (sorting the elements). I (personally) would write it as a standard method.
Then let's make some tests:
var thingsToDo = [
{task: 'wash the dog', priority: 10},
{task: 'do taxes', priority: 1},
{task: 'clean the garage', priority: 3}
];
var tl = new ToyList(['truck', 'plane', 'doll']);
for (var i=0; i<3; i++) {
console.log(tl.toys[i]); // access the *pseudo* toys attribute
}
var todos = new Todos(thingsToDo);
var r = todos.byPriority; // access the *pseudo* byPriority attribute (border effect: sorting internal data)
for (var i=0; i<3; i++) {
console.log(todos.data[i].priority);
}
May I suggest you to have a little more read about OOP and inheritance? The point of need to subclass but removing data attribute is certainly a bad design.

IMO, a better approach, if possible, would be to treat the List class are pure virtual, Meaning you will never create an instance of that class, but only just extend from it.
Pure virtual classes are not supposed to have constructors, and the methods are to assume certain properties exist. However, you could infact use the constructor to set the name that the base class should use for the 'data' property.
class List {
constructor(keyName) { this.keyName = keyName }
sortBy(attr) { return this[this.keyName].sort(...) }
}
class ToyList extends List {
constructor('toys') {
super(toys)
this.toys = toys;
}
}

Related

Clone and update in groovy

In javascript there is an easy way to generate cloned object with some of the fields updated:
const person = {
isHuman: false,
name: "gabe"
};
const person2 = {
...person1,
name: 'alice'
}
Is there a way to do something like in groovy without copying all the fields manually? I am writing testcases where I wanna generate data with one attribute changed at a time.
Can you use #Immutable(copyWith = true)
#Immutable(copyWith = true)
class Person {
String first, last
}
def tim = new Person('tim', 'yates')
def alice = tim.copyWith(first:'alice')
assert tim.first == 'tim'
assert alice.first == 'alice'
https://docs.groovy-lang.org/latest/html/gapi/groovy/transform/ImmutableBase.html#copyWith
There are many ways this can be done.
One is to construct instances by spreading in a map, e.g., given:
class Person { String name; Boolean isHuman; }
An instance can be constructed using the same spread-map operator I linked to:
m1 = [ name: "Gabe", isHuman: true ]
p1 = new Person(*:m1)
println p1
// Person: Gabe, isHuman=true
This avoids actual work. An exception will be thrown if a map key isn't an instance property.
A utility method grabbing (non-synthetic) prop names from a class, and iterates while putting the name/value pairs into a map is also an option (and affords a bit more safety):
def toMap(obj) {
obj.class.declaredFields
.findAll { !it.synthetic }
.collectEntries { [(it.name): obj."$it.name" ] }
}
Now we can construct a "prototype" object, but override properties:
p2 = new Person(*:toMap(p1), name: "Alice")
println p2
// Person: Alice, isHuman=true
There are also libraries that do this type of work.
Depending on your actual usecase it may not be necessary to do anything other than passing a map, however (duck typing).

How to override the key iterator in Typescript?

I have this typescript class (reduced for simplicity):
class Dictionary<T> {
items = [];
add(item: T) {
this.items.push(item);
}
... more code here ...
}
When I instantiate a variabel
var channels = new Dictionary<Channel>();
channels.add(new Channel('name1'));
channels.add(new Channel('name2'));
In another part of code (which I cannot change) the object is used in a loop:
for (var key in channels){ console.log(key);}
I get "items", the member of my object whereas I want to get the content of the items array in the channels object.
So what I want in this case is
[0,1] the keys of the items array.
Is this possible?
You can try to do this by implementing Iterator. That will allow you to dictate how loops over your object work.
Have a look here.
Possibly it will only be possible if you target ES6,
however this question seems to suggest you can just implement a next() method to make it work.
Maybe this solution but there is the length member:
class Dictionary<T> extends Array<T> {
add(item: T) {
this.push(item);
}
}
class Channel {
constructor(public name: string) {
}
}
var channels = new Dictionary<Channel>();
channels.add(new Channel('name1'));
channels.add(new Channel('name2'));
console.log(Object.keys(channels)); // Array [ "0", "1", "length" ]
Updated: To get value of object through key
Try like this
for (var key in channels){
console.log(channels[key]);
}

Working With Array Of Objects

I have an array of objects that can contain children of the same object type, like this:
var exampleArray = [
{
alias: 'alias1',
children: [
{
alias: 'child1'
},
{
alias: 'child2',
children: [
{
alias: 'child4'
},
{
alias: 'child5'
}
]
},
{
alias: 'child3'
}
]
},
{
alias: 'alias2'
},
{
alias: 'alias3',
children: [
{
alias: 'child6'
},
{
alias: 'child7'
}
]
}
];
The base object has other properties but they are not important to the question(s) at hand. For now, lets just assume the objects can be:
{
alias: 'string',
children: []
}
The children are optional.
I am looking for the best methods / fastest methods for managing some things with an object like this. I have created some recursive methods to do some of the things I want, but I want to know if there are better ways to go about doing the following tasks:
hasAlias(arr, alias) - I need to determine if the entire object contains any object with the give alias.
Currently, I do this recursively but given that this array can grow finitely, the recursive method is eventually going to hit stack limits.
getParent(arr, alias) - I need to be able to obtain the parent that contains an element with the given alias. Given that alias' are unique to the entire array, there will never be two of the same alias. Again I do this recursively right now but I want to find better methods of doing this.
deleteObject(arr, alias) - I am unsure how to accomplish this one currently. I need to be able to pass an array and an alias and have that object (and all its children) removed from the given array. I started a recursive method of doing this but stopped and decided to post here instead.
I am using Node.js and have lodash available for faster methods of doing things. I'm still fairly new to JavaScript so I am unsure if there are better ways to go about doing things with larger scale arrays like this.
Back in the days of FORTRAN which didn't support recursion, one achieved similar effects by changing a data set to simulate a level of "recursion". Applying this principle to the example object structure, a function to lookup an object by its "alias" (a name or id by another word) could be written without recursion like this:
function findAlias( parent, alias) // parent object, alias value string
{ function frame( parent)
{ return {parent: parent, children: parent.children,
index: 0, length: parent.children.length};
}
var stack, tos, child, children, i;
stack = [];
if( parent.children)
stack.push( frame( parent));
search:
while( stack.length)
{ tos = stack.pop(); // top of generation stack
children = tos.children;
for( i = tos.index; i < tos.length; ++i)
{ child = children[i];
if( child.alias == alias)
{ return { parent: tos.parent, child: child, childIndex: i}
}
if( child.children)
{ tos.index = i + 1;
stack.push(tos); // put it back
stack.push( frame(child));
continue search;
}
}
}
return null;
}
In short one ends up creating a stack of smallish data objects which are pushed and popped in the same function instead of making recursive calls. The example above returns and object with parent and child object values. The child value is the one with the supplied alias property, and the parent object is the one with the child in its children array.
It returns null if the alias could not be found so can be used for hasAlias functionality. If it doesn't return null it performs the getParent functionality. You do have to create a root node however:
// create a rootnode
var rootNode = { alias: "root", children: exampleArray};
var found = findAlias(rootNode, "alias3");
if( found)
{ console.log("%s is a child of %s, childIndex = %s",
found.child.alias, found.parent.alias, found.childIndex);
}
else
console.log("not found");
[Edit: add childIndex to search return object, update test example code, add conclusion.]
Conclusion
Using recursive function calls when supported for a tree walking application makes sense in terms of self documenting code and maintainability. A non recursive variation may pay for itself it if it can be shown that it has significant benefits in reducing server load under volume pressure tests, but requires sound documentation.
Regardless of internal coding, a tree walking function which returns an object with details of parent, child, and child index values might contribute to overall program efficiency by reducing the total number of treewalks ever performed:
truthiness of the search return value substitutes for an hasAlias function
the return object from the search can be passed to update, remove or insert functions without requiring repeated tree searches in each function.
The guaranteed fastest way is obviously to have
- an index for the aliases (thats actually a unique id)
- have a parent backlink on each child item (if it has a parent)
You look up against the id index
var index = {}
(function build(parent) {
index[parent.alias] = parent;
(parent.children || []).forEach( item => {
item.parent = parent
build(item)
})
})(objectRoot)
function hasAlias(alias) { return alias in index }
function getAlias(alias) { return index[alias] }
function getParent(alias) { return index[alias] && index[alias].parent}
Deleting an alias would mean removing it and its children from the index and from the parent that still remains in the index
function deleteAlias(alias) {
function deleteFromIndex(item) {
delete index[item.alias]
(item.children || []).forEach(deleteFromIndex)
}
var item = index[alias]
item.parent.children.splice(item.parent.children.indexOf(item))
deleteFromIndex(item)
}
I might approach your main array slightly differently, and keep it as a flat array that references other items rather than incorporates them entirely.
var flat = [
{
alias : "str1",
children : [ flat[1], flat[2] ],
parent : null
},
{
alias : "str1",
children : [],
parent : flat[0]
},
{
alias : "str1",
children : [],
parent : flat[0]
}
]
This is kind of a "linked list" approach. There are pros and cons to linked lists, but you'll be able to iterate over all items quickly.

JavaScript, overwrite object without losing reference

Application
I am working on a simple web application that is built on top of AngularJS. The application should be able to work offline as well as online. When the user is offline, the changes to the data is stored locally. Therefore, the id's that is used within this application in offline mode is only temporary id's, they get replaced when uploaded to the server
Problem
The data that are used in the application consists of complex objects (with relations/references to other objects). When i am saving to the server, i wanted the views to get updated with the new "real" id's.
However, since JavaScript works with objects as references im not able to do what i want to: $scope.data = newdata
This is not overwriting $scope.data but creates a new object. The old reference to the old data is still there.
Simplified example
var x = {id: 1, name: "myObject"}
var c = x // c = {id: 1, name: "myObject"}
x = {id: 2, name: "myNewObject"}
// c = {id: 1, name: "myObject"}
As you can see, c is still a reference to the old object. In practice, this causes that my view isn't updated with new data since it's still bound to the old data.
What i need to is to overwrite the properties of, in this example, x. I need to do this recursively since my real objects are complex, however it shouldn't enter any circular references, since this will probably cause stack overflow. If i am overwriting a with b and a has properties that b hasn't got, those properties should be removed.
What i need
I need some sort of function that overwrites all properties in a (old object) with the properties in b (new object). All properties that exists in a but not in b should be removed.
If your environment supports ECMAScript 2015, you can use Object.assign():
'use strict'
let one = { a: 1, b: 2, c: 3 };
let two = { b: 20, c: 30, d: 40 };
let three = Object.assign({}, one, two);
console.log(three);
// will output: Object {a: 1, b: 20, c: 30, d: 40}
(let is the new locally scoped version of var in ECMAScript 2015) more...
So in the case of your simple example:
var x = { id: 1, name: "myObject" };
Object.assign(x, { id: 2, name: "myNewObject" });
console.log(x);
// will output: Object {id: 2, name: "myNewObject"}
Using the "extend" method which is available in underscore and jquery:
//Clear all the 'old' properties from the object
for (prop in old_object) {delete old_object[prop]}
//Insert the new ones
$.extend(old_object, new_object)
I found a solution after some thinking. It's probably not the most efficient solution, but it does the job for me. The time complexity could probably be better, and all suggestions of improvement are welcome. First parameter is the object to be extended, the second the one to extend with. The third is supposed to be a boolean, indicating whether the properties in a that doesn't exist in b should be removed or not.
function extend(_a,_b,remove){
remove = remove === undefined ? false : remove;
var a_traversed = [],
b_traversed = [];
function _extend(a,b) {
if (a_traversed.indexOf(a) == -1 && b_traversed.indexOf(b) == -1){
a_traversed.push(a);
b_traversed.push(b);
if (a instanceof Array){
for (var i = 0; i < b.length; i++) {
if (a[i]){ // If element exists, keep going recursive so we don't lose the references
a[i] = _extend(a[i],b[i]);
} else {
a[i] = b[i]; // Object doesn't exist, no reference to lose
}
}
if (remove && b.length < a.length) { // Do we have fewer elements in the new object?
a.splice(b.length, a.length - b.length);
}
}
else if (a instanceof Object){
for (var x in b) {
if (a.hasOwnProperty(x)) {
a[x] = _extend(a[x], b[x]);
} else {
a[x] = b[x];
}
}
if (remove) for (var x in a) {
if (!b.hasOwnProperty(x)) {
delete a[x];
}
}
}
else{
return b;
}
return a;
}
}
_extend(_a,_b);
}
I'm adding an answer, even though everyone has explained both why and solutions.
The reason I'm adding answer, is because I've searched for this answer a few times over the years and always basically come to the same 2/3 SO questions. I put the solutions in the too-hard-basket, because the code I've been working with has many modules all following similar design patterns; it's just been too much work to try and resolve what boiled down to the same issue you were having.
What I've learned, and hopefully it holds some value for others out there now that I've actually re-factored our codebase to avoid this issue (sometimes maybe its unavoidable, but sometimes it definitely is), is to avoid using 'static private variables' to reference Objects.
This can probably be more genericised, but take for example:
var G = {
'someKey' : {
'foo' : 'bar'
}
};
G.MySingletonClass = (function () {
var _private_static_data = G.someKey; // referencing an Object
return {
/**
* a method that returns the value of _private_static_data
*
* #method log
**/
log: function () {
return _private_static_data;
} // eom - log()
}; // end of return block
}()); // end of Class
console.log(G.MySingletonClass.log());
G.someKey = {
'baz':'fubar'
};
console.log(G.MySingletonClass.log());
http://jsfiddle.net/goxdebfh/1/
As you can see, same problem experienced by the Questioner. In my case, and this use of private static variables referencing Objects was everywhere, all I needed to do was directly lookup G.someKey; instead of storing it as a convenience variable for my Class. The end result (though lengthier as a result of inconvenience) works very well:
var G = {
'someKey' : {
'foo' : 'bar'
}
};
G.MySingletonClass = (function () {
return {
/**
* a method that returns the value of _private_static_data
*
* #method log
**/
log: function () {
return G.someKey;
} // eom - log()
}; // end of return block
}()); // end of Class
console.log(G.MySingletonClass.log());
G.someKey = {
'baz':'fubar'
};
console.log(G.MySingletonClass.log());
http://jsfiddle.net/vv2d7juy/1/
So yeah, maybe nothing new given the question has been solved, but I felt compelled to share that because I was even lead to believe that the first example was the correct way to do things. Maybe in some cases it is, it definitely didn't turn out to be.
Hopefully that helps someone, somewhere!

In Javascript, given value, find name from Object literal

I'm new JavaScript and trying to find out an easier way to find name given a value from object literal.
e.g.
var cars ={ Toyata: ['Camry','Prius','Highlander'],
Honda: ['Accord', 'Civic', 'Pilot'],
Nissan: ['Altima', 'Sentra', 'Quest']};
Given 'Accord', I want to get Honda from the object Cars.
You would need to loop through, like this:
function getManufacturer(carName) {
for(var key in cars) {
if(cars.hasOwnProperty(key)) {
for(var i=0; i<cars[key].length; i++) {
if(cars[key][i] == carName) return key;
}
}
}
return "Not found";
}
You can test it out here, for the same of working cross-browser, this ignores the existence of .indexOf() since IE doesn't have it...that version would look like this:
function getManufacturer(carName) {
for(var key in cars) {
if(cars.hasOwnProperty(key) && cars[key].indexOf(carName) != -1) {
return key;
}
}
return "Not found";
}
If you're going to be doing this once, then use a function like the one given by Bobby. If you're going to be doing this multiple times then I'd suggest creating a reverse mapping of cars to manufacturers:
var manufacturers = {};
// create a map of car models to manufacturers:
for (var manf in cars) {
/* see note below */
for (var i=0; i<cars[manf].length; i++) {
manufacturers[cars[manf][i]] = manf;
}
}
// Now referencing the manufacturers is
// a very fast hash table lookup away:
var model = 'Accord';
alert(manufacturers[model]);
note for those with itchy downvoting fingers: For objects that don't inherit anything as given in the OP a hasOwnProperty check here is unnecessary. For objects that do inherit it depends on the programmer. If you want composability via inheritance then a hasOwnProperty check is exactly what you DONT want. If you don't care about inheritance then use a hasOwnProperty check but if so you would not be inheriting in the first place which would make a hasOwnProperty check unnecessary. In the rare case where you are forced to create the object via inheritance but don't want to check the parent's attributes then you should do a hasOwnProperty check. Of course, if you use a library like Prototype.js that insists on modifying the Object object then I feel sorry for you because you are forced to do a hasOwnProperty check.
Maintain a separate mapping of models to manufacturers.
var cars ={ Toyata: ['Camry','Prius','Highlander'],
Honda: ['Accord', 'Civic', 'Pilot'],
Nissan: ['Altima', 'Sentra', 'Quest']};
var models = {};
var hasOwnProperty = Object.prototype.hasOwnProperty;
for (key in cars) {
if (hasOwnProperty.call(cars, key)) {
var i=0,l=cars[key].length,manufacturer=cars[key];
while (i<l) {
if ( ! hasOwnProperty.call(models, manufacturer)) {
models[manufacturer] = key;
} else {
// Throw an error, or change the value to an array of values
}
i++;
}
}
}

Categories

Resources