How to create an object from constructor data at class initialisation - javascript

I want it to be the case that when I instantiate an instance of the class TableCategory, an object is created using the data passed in at instantiation. I want this to happen automatically at instantiation. I don't want have to instantiate the class and then call a method that creates the object. This seems unnecessary cumbersome.
I am using a class because I then want to manipulate the resultant object using getters and setters for multiple instantiations. (In case you wonder why I'm not just using an object in the first place.)
I'm not 100% clear on classes in JS so I'm not sure where I'm going wrong. Please note the object creation is a the product of a function it takes an array passed in at instantiation and an array that is native to the class.
Here's my class:
export class TableCategory {
constructor(categoryValues = []) {
this.categoryValues = categoryValues;
this.categoryKeys = ['alpha','beta','gamma', 'delta'];
this.categoryData = this.categoryKeys.forEach(function(key, i) {
return this.categoryData[key] = this.categoryValues[i];
});
}
}
Then, for example:
const foo = new TableCategory(['a'. 'b', 'c', 'd']);
console.log(foo.categoryData.beta); // b
Perhaps I need to use static ? Not sure, grateful for any help

forEach() doesn't return anything. Create an empty categoryData object, and then fill it in in the forEach loop.
Also, you need to use an arrow function to be able to access this in the callback function.
class TableCategory {
constructor(categoryValues = []) {
this.categoryValues = categoryValues;
this.categoryKeys = ['alpha', 'beta', 'gamma', 'delta'];
this.categoryData = {};
this.categoryKeys.forEach((key, i) =>
this.categoryData[key] = this.categoryValues[i]
)
}
}
const foo = new TableCategory(['a', 'b', 'c', 'd']);
console.log(foo.categoryData.beta); // b

forEach does not return anything else than undefined. You can still get the desired object in a functional way, but with Object.fromEntries.
Also, as you say you use a class because you intend to mutate the instance(s) with getters and setters, I don't think it is a good idea to still store the values and keys separately from the third property, which has all key/value information.
You could for instance do this:
class TableCategory {
constructor(values = []) {
this._obj = Object.fromEntries(['alpha','beta','gamma', 'delta']
.map((key, i) => [key, values[i]]));
}
get values() {
return Object.values(this._obj);
}
get keys() {
return Object.keys(this._obj);
}
}
let obj = new TableCategory(["this", "is", "a", "test"]);
console.log(obj.values);

Related

Component array property call by reference in angular 8

I have a method in an angular component that pass component property array in method, but property value is not changing:
private districtModel : LocationModel[] = [];
valueChange(apiService : ApiService,control : FormControl,url,targetModel: LocationModel[]) {
control.valueChanges.subscribe(newValue=>{
if(control.value === ""){
console.log("empty");
console.log(this.stateTemp);
this.stateModel = this.stateTemp;
}
else{
this.stateModel = this.filterValues(newValue,this.stateModel);
}
if(this.stateModel.length===1){
console.log(this.stateModel[0].id);
apiService.GetLocationById<LocationModel[]>(url,this.stateModel[0].id)
.subscribe(data=> {
targetModel = data;
//console.log(this.districtModel);
});
}
});
}
Function calling
this.valueChange(apiService,this.societyForm.controls['State'] as
FormControl,"https://localhost:44355/Location/GetDistrict?StateId=",this.districtModel);
I want to change the value that is this.districtModel inside function but it's not changing
Here
targetModel = data;
you assign data to the local variable targetModel which will not change this.districtModel. What you could do, is clear the original array and assign new values to it:
targetModel.splice(0, targetModel.length, ...data);
This removes targetModel.length items from the array beginning at 0 and adds all items from data.
If your object LocationModel contains nested objects, you can deep copy your array with lodash, before passing it to the function:
import * as _ from "lodash";
// ...
const clonedDistrictModel = _.cloneDeep(districtModel);
If you only need shallow copy, use the spread operator like that:
const clonedDistrictModel = { ...districtModel }
Take a look here if you want to know different types of copy: https://www.freecodecamp.org/news/copying-stuff-in-javascript-how-to-differentiate-between-deep-and-shallow-copies-b6d8c1ef09cd/
You can deep copy object to another object the following code.
JSON.parse(JSON.stringify(Object))

Typescript/JSX - Assign an instance of a class by reference

Essentially, I want to be able to access an object's property by reference. Take a look at the code below;
class Point{
x:number;
y:number;
constructor(x,y)
{
this.x=x;
this.y=y;
}
}
const a = { first: new Point(8,9), second: new Point(10,12) };
let someBool = true;
function modifyProperty(a) {
let c = someBool? a.first: a.second;
let newPoint = new Point(0,0);
c = newPoint; // Doesn't work
someBool = !someBool;
}
modifyProperty(a);
console.log(a.first);
In this example, whenever I call modifyProperty() I want to alternate between changing one of the two properties in 'a'.
However, when I assign 'c' to either 'a.first' or 'a.second', it only passes by value. The only way I could think to fix this is by making the property itself an object, like this:
const a = { first: {value: new Point(8,9)}, second: {value: new Point(10,12)} };
And then I would just call c.value = newPoint instead. This would work, but it's not a good solution, since you'd have to do this for every property in an object.
Is there no way better way to get these properties by reference? I know JS only supports pass-by-reference for objects and arrays, but what about instances of classes?
I know when Babel converts a class to normal Javascript they're treated like functions, but a function is not a primitive type - it's an object that is callable, so doesn't this work, and what would be a solution?
However, when I assign 'c' to either 'a.first' or 'a.second', it only passes by value
Yes, assignment always changes the value of the thing on the left side of =,
there is no way to change it in Javascript or TypeScript.
One workaround is to use property name together with the object to which the property belongs, instead of reference:
type Pair<T> = { first: T, second: T }
function modifyProperty(a: Pair<Point>) {
let c: keyof Pair<Point> = someBool? 'first' : 'second';
// keyof Pair<Point> type annotation means
// that only property names of Pair could be assigned to c
let newPoint = new Point(0,0);
a[c] = newPoint;
someBool = !someBool;
}

javascript Object - access child property using dot notation

I created an object that requires a dictionary object be passed to initialize, and has attributes (like length). I'd like to be able to simply access the dictionary's key : value pair by passing something like:
myNewObj['a'] or myNewObj.a
and not having to instead pass myNewObj.dictionary['a'], but can't figure out how to do so.
Is there a way to set a dynamic attribute that, if no other attribute or method exits, will instead look into the dictionary and find the associated key?
var newObject = function(dictionary) {
this.dictionary = dictionary;
this.length = dictionary[[Object.keys(dictionary)[0]]].length;
this.get = function (column) {
return this.dictionary[[column]];
}
};
var myNewObj = new newObject({
a : [1,2,3],
b : [4,5,6]
});
console.log(myNewObj.get('a'));
I've updated this to show a .get() method that works, but it's still not exactly what I'm looking for.
jsFiddle: http://jsfiddle.net/2uMjv/571/
Although this might not exactly fit your use case of dynamic attributes, the closest thing I can think of is extending the prototype. At least by using Object.setPrototypeOf* you can avoid defining each method individually and have a base set of functions to apply to each one of these newObject's.
var newObject = function(dictionary) {
this.dictionary = dictionary;
this.length = dictionary[[Object.keys(dictionary)[0]]].length;
this.get = function(column) {
return this.dictionary[[column]];
}
};
// Essentially a wrapper constructor.
var createNewObject = function(dictionary) {
Object.setPrototypeOf(dictionary, new newObject(dictionary));
return dictionary;
}
var myNewObj = createNewObject({
a : [1,2,3],
b : [4,5,6]
});
console.log(myNewObj['a']);
console.log(myNewObj.get('b'));
*You may want to look at the potential drawbacks in using this.
Why don't you just set properties? :
class Dictionary {
constructor(dictionary) {
Object.assign(this, dictionary);
}
get length() {
return Object.keys(this).length;
}
get(column) {
return this[column];
}
}
var dict = new Dictionary({
a : [1,2,3],
b : [4,5,6]
});
console.log(dict.a, dict["a"], dict.get("a"));

using replace logic in both arguments

I basicly need to do some replace logic to change the '.' to a '>' at here everything fine, i can just use the replace method from javascript, but i am searching the best way to do it.
Basicly i will use this function just for 1 specific task nothing more, i want to do this replace logic to my Name propertiy and description inside the object, so instead of doing a simple method that does the replace i need to pass it two times down.
At the moment i have this repeated: element.Name.replace('.', ' > ')
i created a method, but i thaught as the best possible way to maybe pass it to the function like: replaceMethod(firstProp,secondProp) where each prop gets replaced, so how can i inside the replace method just apply the same logic to all my arguments without using a useless for loop?
something like this:
replaceMethod(firstProp,secondProp) {
allArgs.replace('.', ' > ')
}
i did this:
callerFunc() {
// service get the object material, it has a name and description with '.'
replaceMethod(material,material.Name,material.Description)
// do some logic after the method with the material
}
replaceMethod(material,...keys) {
keys.forEach(k => material[k] = material[k].replace(/\./g, ' > '));
}
In ES6, you could use rest parameters ... for collecting all arguments.
function replaceMethod(...keys) {
keys.forEach(k => object[k] = object[k].replace(/\./g, ' > '));
}
var object = { name: 'foo.bar.baz', town: 'st.peter' };
replaceMethod('name', 'town');
console.log(object);
ES5 with use of arguments object.
function replaceMethod() {
Array.prototype.forEach.call(arguments, function (k) {
object[k] = object[k].replace(/\./g, ' > ');
});
}
var object = { name: 'foo.bar.baz', town: 'st.peter' };
replaceMethod('name', 'town');
console.log(object);
I would recommend passing the object as a parameter with the keys of the properties you want to change. Then return a new object with the changes instead of changing the object in place. This is a more functional approach without side effects. You can use the array reduce method. It is most convenient in ES6 using the spread operator ...
function replaceForKeys(obj, ...keys) {
return keys.reduce(
function (result, k) {
return { ...result, [k]: obj[k].replace(/\./g, ' > ') };
},
Object.assign({}, obj)
);
}
var obj = { 'foo': 'foo.bar', 'bar': 'bar.foo' };
var replaced = replaceForKeys(obj, 'foo', 'bar');
So the function takes every argument after the object as an array of keys and reduces over them returning the original object with the property replaced each time. The reduce method takes an initial value as the second parameter and in this case we use Object.assign to use a copy of the original object as the initial value. the [k]: syntax in the object is new in ES6 I believe and is a computed key. It just lets you assign keys in the object without knowing their value beforehand.

Read variable number of properties from object

Say I have this function signature:
export const readVariableProps = function(obj: Object, props: Array<string>) : any {
// props => ['a','b','c']
return obj['a']['b']['c'];
}
obviously, props is a variable length array, with an unknown list or properties to read from the given object.
is the only way to get this kind of dynamic behavior to use eval()?
How can I do this?
To get the equivalent of return obj['a']['b']['c']; where 'a', 'b' and 'c' are the values in the array like you show in your question, you can do something like this (you may have to convert some details to typeScript):
export const readVariableProps = function(obj: Object, props: Array<string>) : any {
return props.reduce(function(prior, next) {
return prior[next];
}, obj);
}
FYI, this type of scenario is exactly what .reduce() was designed for -
accumulating a value that is built by visiting every item in an array.
This will throw if anything other than the last property in the array does not exist.
You can use a for..of loop to set a variable to each nested property
const props = ['a','b','c'];
const obj = {a:{b:{c:123}}};
let res;
for (let prop of props) res = !res ? obj[prop] : res[prop];
console.log(res);

Categories