Adding meta data to a primitive in javascript - javascript

Background
We have much of our data formatted like
var X = {value:'some val',error:'maybe an error',valid:true}
as a result we find ourselves calling X.value ALL the time.
We don't use the .error or .valid nearly as much, but we do use it.
What I want
To quit calling .value everywhere, but to still have access to meta data on a per data point level.
The Question
Is there one of
A) A way to put meta data on a primitive? attaching .error to an int for example? Is it possible for bools or strings?
B) A way to make a class that can be treated as a primitive, providing a specific data member when I do? IE X.value = 5, X+3 returns 8.
C) A better design for our data? Did we just lay this out wrong somehow?

You can set the method toString() to your object and return value.
var X = {
value: 1,
error:'maybe an error',
valid:true,
toString: function() {
return this.value;
}
}
X.value = 5;
console.log(X+3);

You can represent you data as a function object that also has properties:
var X = () => 1;
X.value = 1;
X.error = 'maybe an error';
X.valid = true,
console.log(X()); // 1
console.log(X.valid); // true
For better design you can encapsulate the creation of the data object in another function.

Related

Pass object and properties by reference in Javascript

I've seen lots of questions about passing objects by reference in Javascript, but not the object and properties by reference. Is it possible?
Right now I only found a way to do it by going through some type of logic like this, which is terribly inconvenient:
let multipliers = {
none:1,
sin:2,
cos:3,
tan:4,
atan:5,
}
incMultiplier(shapesMovements[index], "rotation", "x", "sin")
function incMultiplier(shapeMovement, kind, dimension, multiplier){
var numOfKeys = Object.keys(multipliers).length;
if(kind === "rotation"){
if(dimension === "x"){
if(multiplier === "sin"){
if(shapeMovement.rotation.x.multiplier !== numOfKeys){
shapeMovement.rotation.x.multiplier += 1
}else{
shapeMovement.rotation.x.multiplier = 1
}
}
}
}
}
I'd just like to increase the property value by one with whatever object and property I've thrown into that function.
I've seen another post where you can pass parameters, but this looks to assemble a new object, and is not by reference. I need to actually edit the values on the object's properties.
Originally, this is what I was trying, and it did not seem to alter the object on a global level. Only locally to the function:
incMultiplier(shapesMovements[index].rotation.x.multiplier)
function incMultiplier(multiplier){
var numOfKeys = Object.keys(multipliers).length;
if(multiplier !== numOfKeys){
multiplier = multiplier + 1
}else{
multiplier = 1
}
// always results in the same number.
// Does not keep increasing every time the function is called.
console.log(multiplier);
}
Originally, this is what I was trying
You're not passing an object with its properties there. You're passing the value of a single property, and assignments to multiplier do indeed just overwrite the local variable in the function. You need to pass an object and explicitly assign to its property:
function incMultiplier(valueObj) {
var numOfKeys = Object.keys(multipliers).length;
if (valueObj.multiplier !== numOfKeys) {
valueObj.multiplier++;
} else {
valueObj.multiplier = 1
}
}
incMultiplier(shapesMovements[index].rotation.x)
incMultiplier(shapesMovements[index].position.x)
incMultiplier(shapesMovements[index].rotation.y)
incMultiplier(shapesMovements[index].rotation.z)
It's not necessary to pass the whole shapesMovements objects and everything nested within them, passing a single mutable object is enough.

Comparing 2 Json Object using javascript or underscore

PS: I have already searched the forums and have seen the relevant posts for this wherein the same post exists but I am not able to resolve my issue with those solutions.
I have 2 json objects
var json1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222", addrs:"def", tab:"tab2"}];
var json2 = [{id:"tab1"},{id:"new"}];
I want to compare both these and check if the id element in json2 is present in json1 by comparing to its tab key. If not then set some boolean to false. ie by comparing id:"tab1" in json2 to tab:"tab1 in json1 .
I tried using below solutions as suggested by various posts:
var o1 = json1;
var o2 = json2;
var set= false;
for (var p in o1) {
if (o1.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
for (var p in o2) {
if (o2.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
Also tried with underscore as:
_.each(json1, function(one) {
_.each(json2, function(two) {
if (one.tab!== two.id) {
set= true;
}
});
});
Both of them fail for some test case or other.
Can anyone tell any other better method or outline the issues above.
Don't call them JSON because they are JavaScript arrays. Read What is JSON.
To solve the problem, you may loop over second array and then in the iteration check if none of the objects in the first array matched the criteria. If so, set the result to true.
const obj1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222",addrs:"def", tab:"tab2"}];
const obj2 = [{id:"tab1"},{id:"new"}];
let result = false;
for (let {id} of obj2) {
if (!obj1.some(i => i.tab === id)) {
result = true;
break;
}
}
console.log(result);
Unfortunately, searching the forums and reading the relevant posts is not going to replace THINKING. Step away from your computer, and write down, on a piece of paper, exactly what the problem is and how you plan to solve it. For example:
Calculate for each object in an array whether some object in another array has a tab property whose value is the same as the first object's id property.
There are many ways to do this. The first way involves using array functions like map (corresponding to the "calculate for each" in the question, and some (corresponding to the "some" in the question). To make it easier, and try to avoid confusing ourselves, we'll do it step by step.
function calculateMatch(obj2) {
return obj2.map(doesSomeElementInObj1Match);
}
That's it. Your program is finished. You don't even need to test it, because it's obviously right.
But wait. How are you supposed to know about these array functions like map and some? By reading the documentation. No one help you with that. You have to do it yourself. You have to do it in advance as part of your learning process. You can't do it at the moment you need it, because you won't know what you don't know!
If it's easier for you to understand, and you're just getting started with functions, you may want to write this as
obj2.map(obj1Element => doesSomeElementInObj1Match(obj1Element))
or, if you're still not up to speed on arrow functions, then
obj2.map(function(obj1Element) { return doesSomeElementInObj1Match(obj1Element); })
The only thing left to do is to write doesSomeElementInObj2Match. For testing purposes, we can make one that always returns true:
function doesSomeElementInObj2Match() { return true; }
But eventually we will have to write it. Remember the part of our English description of the problem that's relevant here:
some object in another array has a tab property whose value is the same as the first object's id property.
When working with JS arrays, for "some" we have the some function. So, following the same top-down approach, we are going to write (assuming we know what the ID is):
In the same way as above, we can write this as
function doesSomeElementInObj2Match(id) {
obj2.some(obj2Element => tabFieldMatches(obj2Element, id))
}
or
obj2.some(function(obj2Element) { return tabFieldMatches(obj2Element, id); })
Here, tabFieldMatches is nothing more than checking to make sure obj2Element.tab and id are identical.
We're almost done! but we still have to write hasMatchingTabField. That's quite easy, it turns out:
function hasMatchingTabField(e2, id) { return e2.tab === id; }
In the following, to save space, we will write e1 for obj1Element and e2 for obj2Element, and stick with the arrow functions. This completes our first solution. We have
const tabFieldMatches = (tab, id) { return tab === id; }
const hasMatchingTabField = (obj, id) => obj.some(e => tabFieldMatches(e.tab, id);
const findMatches = obj => obj.some(e => hasMatchingTabField(e1, obj.id));
And we call this using findMatches(obj1).
Old-fashioned array
But perhaps all these maps and somes are a little too much for you at this point. What ever happened to good old-fashioned for-loops? Yes, we can write things this way, and some people might prefer that alternative.
top: for (e1 of obj1) {
for (e2 of (obj2) {
if (e1.id === e2.tab) {
console.log("found match");
break top;
}
}
console.log("didn't find match);
}
But some people are sure to complain about the non-standard use of break here. Or, we might want to end up with an array of boolean parallel to the input array. In that case, we have to be careful about remembering what matched, at what level.
const matched = [];
for (e1 of obj1) {
let match = false;
for (e2 of obj2) {
if (e1.id === e2.tab) match = true;
}
matched.push(match);
}
We can clean this up and optimize it bit, but that's the basic idea. Notice that we have to reset match each time through the loop over the first object.

Why does map work differently when I return an array instead of some primitive in the callback function?

Script
var companies=[
{name:'Vicky',category:'Devdas',start:1993,end:2090},
{name:'Vikrant',category:'Devdas',start:1994,end:2019},
{name:'Akriti',category:'mental',start:1991,end:2021},
{name:'Dummy',category:'dummyCategory',start:1995,end:2018},
{name:'Dummy 1',category:'dummyCategory',start:1993,end:2029}
];
var mappingComp=companies.map(company=>{company.start+10;return company});
console.log("mapped company function");
console.log(mappingComp.forEach(company=>console.log(company)));
In the above snippet there is no change in start field of companies array . Why ?
In case I do below I do get modified values for start field from companies array.
var mappingComp=companies.map(company=>company.start+10);
You aren't assigning the result of company.start+10 to anything - it's just an orphaned expression.
var mappingComp = companies.map(company => {
company.start + 10;
return company
});
is just like
var mappingComp = companies.map(company => {
33;
return company
});
The expression is evaluated to a value and then discarded. If you want to add 10 to company.start, use += or =:
var companies=[
{name:'Vicky',category:'Devdas',start:1993,end:2090},
{name:'Vikrant',category:'Devdas',start:1994,end:2019},
{name:'Akriti',category:'mental',start:1991,end:2021},
{name:'Dummy',category:'dummyCategory',start:1995,end:2018},
{name:'Dummy 1',category:'dummyCategory',start:1993,end:2029}
];
var mappingComp = companies.map(company => {
company.start += 10;
return company;
});
console.log(mappingComp);
But this will mutate the original array, which is (often) not a great idea when using map. If you don't want to change the original array, map to a new object:
var companies=[
{name:'Vicky',category:'Devdas',start:1993,end:2090},
{name:'Vikrant',category:'Devdas',start:1994,end:2019},
{name:'Akriti',category:'mental',start:1991,end:2021},
{name:'Dummy',category:'dummyCategory',start:1995,end:2018},
{name:'Dummy 1',category:'dummyCategory',start:1993,end:2029}
];
var mappingComp = companies.map(({ start, ...rest }) => ({
start: start + 10,
...rest
}));
console.log(mappingComp);
company.start + 10 is a simple expression. It's not an assignment statement, that you are expecting it to be. And you are returning the initial array company so it makes sense that it will be returned unaltered.
when you tried the single line fat arrow function with the map. What happens is that you created another entirely different array of mutated values. The array created was populated with values (company.start +10) and returned. Note: This actually didn't change the initial array ie company.
Read up on fat arrow functions, map, filter.

Library or object that can check containment of lists

This question is an extension of this one: Checking containment in set of lists in javascript. I want to be able to use a set like function in nodejs or Javascript that can support checking whether or not a list belongs to a collection. For example, given the example in the link, I would like the behavior:
var s = new SetWithListCheckingAbility([[1,2], [2,3]])
s.has([2, 3])
true
I was unable to find any nodejs library that has this functionality, however. The other obvious solution seems to be JSON serializing each object that is added to the set object, and doing checking based on the JSON string, since Javascript equality works for strings. This would probably require subclassing the Set object in ES6. However, I am not sure how to do this for this case...
What you can do is take each member of the set and convert it to a string format (this answer looks like an elegant way to do that conversion from numbers to strings).
For your example, if you want s.has([3, 2]) to return false because [2,3] doesn't count as a match, the array to string conversion would look like array.join(','), otherwise array.sort().join(',') if order doesn't matter.
function setOfListsHasElement(theSet, theElement) {
let newSet = new Set();
theSet.forEach(e => newSet.add(e.join(',')) );
return newSet.has(theElement.join(','));
}
Example usage:
var theSet = new Set();
theSet.add([1,2]);
theSet.add([2,3]);
setOfListsHasElement(theSet, [2,3]); // true
setOfListsHasElement(theSet, [3,2]); // false
setOfListsHasElement(theSet, [2,6]); // false
setOfListsHasElement(theSet, ["1", "2"]); // true - don't know how you want to handle scenarios like this, where the string representation of ["1", "2"] matches that of [1,2]
I figured out how to write a custom class that does what we want:
class SetImproved extends Set{
constructor(){
super();
this.classDict = {};
this._size = 0;
}
get size(){
return this._size
}
add(x){
if(!(JSON.stringify(x) in this.classDict)){
this._size += 1;
}
this.classDict[JSON.stringify(x)] = x;
}
has(x){
return JSON.stringify(x) in this.classDict;
}
delete(x){
if(JSON.stringify(x) in this.classDict){
this._size -= 1;
}
delete this.classDict[JSON.stringify(x)];
}
clear(){
this.classDict = {};
}
keys(){
return Object.keys(this.classDict).map(x => this.classDict[x]);
}
entries(){
return Object.keys(this.classDict).map(x => this.classDict[x]);
}
}
Some examples of the functionality:
var setImproved = new SetImproved()
setImproved.add([1, "b"])
setImproved.add([2, "c"])
setImproved.add(3)
setImproved.add("asdf")
console.log(setImproved.has([1, "b"]))
console.log(setImproved.has([3]))
setImproved.delete([4])
setImproved.delete([1, "b"])
console.log(setImproved.has(3))
console.log(setImproved.entries())
console.log(setImproved.size)

How can I compare a string to an object key and get that key's value?

I want to do something relatively simple, I think anyways.
I need to compare the pathname of page with an object's kv pairs. For example:
if("pathname" === "key"){return value;}
That's pretty much it. I'm not sure how to do it in either regular Javascript or jQuery. Either are acceptable.
You can see my fiddle here: http://jsfiddle.net/lz430/2rhds1x3/
JavaScript:
var pageID = "/electrical-electronic-tape/c/864";
var pageList = [{
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
}];
var label = pageID in pageList;
$('.el').html(label);
First, your "pageList" should just be a plain object, not an object in an array:
var pageList = {
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
};
Then you can set "label" to the value from the mapping:
var label = pageList[pageID] || "(not found)";
That last bit of the statement above will set the label to "(not found)" if the lookup fails, which may or may not be applicable to your situation.
It depends kinda on the logic you want to implement. If you want to say "if object has the key, then do X, and if not, then do Y", then you handle that differently than "set label to the object's key's value if the key is there, or else set it to undefined or something else".
For the first case you do:
if (pageList.hasOwnProperty(pageID) ) {
label = pageList[pageID];
}
else {
// do whatever, maybe some error?
}
For the second case, you can just say
var label = pageList[pageID] || 'notFound';
As indicated by #Pointy, either get rid of the array or subsiture pageList[0] for pageList and pageList[0][pageID] for pageList[pageID] above, if you need to keep the array.

Categories

Resources