Can you create object property names using template literals in javascript? - javascript

I'm attempting to write a function that takes an array of Strings as its input. The function should return an object with (1) key, value pair. The first element of the array should be the property name and the last element of the array should be its key.
function transformFirstAndLast(array){
return {`${array[0]}`: array[length-1];}
}
The above gives me an error. Can someone provide a detailed explanation why this isn't working? I'd like to avoid creating separate variables to store the values from the first and last array indices.

Your question really boils down to, "Can I use expressions as keys in object literals?"
The answer is yes (since es6):
function yell(template, ...parts) {
return parts[0] + '!';
}
function foo() {
return 'bar';
}
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
toString() {
return `${this.first} ${this.last}`;
}
}
let names = ['Abe'];
let my_obj = {
[3+5]: 'some_value',
[yell `${foo()}`]: foo(),
[names[0]]: 64,
[new Person('Rafael', 'Cepeda')]: 25
};
console.log(my_obj);
As long as the expression evaluates to a string, all is fair.

You are missing the { and you can't use a template strings as a key. To use a "variable" as a key in an object, you should use the brakets around the variable. Here's the working code.
function transformFirstAndLast(array){
return {[array[0]]: array[array.length-1]};
}

I guess template literal is unnecessary here simply try like this way after fixing this line array[length-1] because its wrong, correct one is array[array.length-1]. I've added more verbose code but you can also do the shorthand version like return {[array[0]]: array[array.length-1]}; on your transformFirstAndLast(array) function.
function transformFirstAndLast(array){
const result = {};
result[array[0]] = array[array.length-1];
return result;
}
console.log(transformFirstAndLast(['a','b','c','d','e']))

yes you can use string literals as key. But the thing is that they are calculated in the run time. So you need to treat them as expressions. And to have variables/expressions as your keys you need to wrap them inside []
let a = {
`key`: value
} // is not allowed
let a = {
[`key`]: value
} // is allowed since you have wrapp
for your case
return {[`${array[0]}`]: array[array.length-1]};
Now since you have wrapped the array[0] item inside a string literal, you will get string values for your zero'th item. If your array[0] is to be a object this would not work as well. It needs to be either string or number. Or else you would get "[object Object]" as your key

var input = ["name", "fname", "lname", "stackOverflow"];
function transformFirstAndLast(array){
return {[array[0]]: array.pop()}
}
responseObject = transformFirstAndLast(input)
console.log(responseObject)

Related

Print JSON path and variable result in one call in JavaScript

Is it possible to console.log something like this:
myParent.myChildData(5)
(variable literal name + value in brackets)
from a JSON object such as this:
{myParent: {myChildData: 5}}
I would like to do it with referencing the object notation ideally only once. Something like:
console.log(printExpression(myParent.myChildData))
Where printExpression I'm certainly happy to be a generic helper function that could return this. I've searched high and low, but obviously printExpression receives the actual evaluated value and this causes a road block.
You can turn JSON into a JavaScript object by using JSON.parse(jsonString).
You can store that as a variable and then console.log it.
Or you can just directly console.log the passed data like this:
console.log(JSON.parse('{"myparent":{"myChildData": 5}}').myParent.myChildData);
Edit
After understanding what exactly the helper function does, I've created a printExpression function that returns string values based on your example.
function printExpression(object, stringBefore) {
//Recursively make objects with keys as methods
let newObject = {};
for (var key in object) {
//Make sure the key exists on the object
if (object.hasOwnProperty(key)) {
let value = object[key];
//If the value is an object, just add a get method that returns the object
if (typeof(value) == "object") {
let childObject = printExpression(value, key + ".");
newObject[key] = childObject;
}
//If not, make a method that returns the wanted syntax
else {
//Form the string based on specific syntax
let str = key + "(" + value + ")";
//Check if we should add stringBefore
if (stringBefore) {
str = stringBefore + str;
}
newObject[key] = str;
}
}
}
//Return the new object
return newObject;
}
var example = printExpression(JSON.parse('{"myParent": {"myChildData": 5}}'));
console.log(example.myParent.myChildData);
How It Works
When creating the helper object, it recursively reads all the keys of the original object and makes a new object that returns the keys in an organized way. For example if the original object was { greeting: "hello" } then newObject.greeting would be "greeting(hello)" (as you said it should be).
Possible Problems
Doesn't get updated when you change the original object. I don't think this will be much of a problem as you seem to be reading static JSON data, but just letting you know.

Object.prototype.toString() - trying to get 'String' instead of [object String]

I am trying to get the type of an argument by not using the typeof. Also (its part of an exercise) I must use Object.prototype.toString...
Calling the function should return (for example if its a string) "String". However, my code returns String]
How can I remove the bracket?
Thanks!
function types(x){
var array = Object.prototype.toString.call(x);
var arr= array.split(" ");
return arr[1];
}
types("hello");
I don't know why you have to use Object.prototype.toString as native string is not an object. If you force it on a string, then it will return the "[object String]".
If you want the type of a variable, then you can simply use the following function
var types = x => x.contructor.name;
If you must, then you use the following
var types = x => Object.prototype.toString.call(x).match(/\[object (.*)\]/)[1];
This is inefficient as you first wrap the original variable with an object variable, then call its toString function, followed by a regExp (or substr).
This will return String. Uses slice() to remove the trailing ]
function types(x) {
var array = Object.prototype.toString.call(x);
var arr = array.split(" ");
return arr[1].slice(0, -1);
}
console.log(types('hello')) // String

Javascript String replacer for custom template

I would like to get some ideas how to achieve the following task.
I'm writing a lightweight template language. That takes any array or json object and replaces the string-values with values from my local data store.
Please let me illustrate how it works:
var obj = {
prop: "_p{propnameA}",
secondprop: "_p{propnameB}",
thirdprop: "Hello, this is \"_p{propnameC}\" and _p{propnameD},
arr: [
"textvalue", "propB value = _p{propB}"
]
}
I've wrote an algorithm that iterates over each property of each json or array. Now i need a fast way to replace all my template-tags to their actual values.
I would like to use different types of template-tags:
_p{...}
_c{...}
_v{...}
etc.
each template-tag means something different in my program. For Example: the template-tag _p{} calls a method in my application with the parameter of the tag-value. _p{propval} is a equivalent to myApp.getProperty("propval")
Other tags call other methods of my application.
I am thinking about using a string.replace with a regular expression for my tags. But i run into two problems:
How to write this regular expression?
How to handle non-string return values?
the evaluated value of a tag must not always be a string. it could also be a more complex data type like an array or json object. In my first example code at the top of this question the resulting value for "_p{propnameA}" could be an array like [1,2,3,4]. Or _p{propnameB} could be a number and so my example on top should evaluate like:
obj = {
prop: [1, 2, 3, 4],
secondprop: 827,
thirdprop: "Hello, this is \"valueC\" and valueD",
arr: ["textvalue", "propE value = 827"]
}
obviously obj.secondprop should not have the string value "827" but the number instead while obj.arr[1] should be a string.
Do you got some smart ideas how to do this?
Thank you very much for any help!
If I understood correctly, you're looking for something like this:
// evaluate a single placeholder like _p{foo}
function evalPlaceholder(prefix, content) {
switch(prefix) {
case "_p": do_this();
case "_c": do_that();
//etc
}
}
// eval a string with placeholders
function evalTemplate(str) {
var m = str.match(/^(_\w){([^{}]+)}$/);
if(m) {
// the whole string _is_ a placeholder
return evalPlaceholder(m[1], m[2]);
}
// otherwise, the string can _contain_ placeholders
return str.replace(/(_\w){(.+?)}/g, function(_, $1, $2) {
return evalPlaceholder($1, $2);
});
}
// walk an object recursively and eval all templates
function evalObject(obj) {
Object.keys(obj).forEach(function(k) {
var v = obj[k];
if(typeof v == "object")
evalObject(v)
else
obj[k] = evalTemplate(v);
})
}
first, you can cast numeric values to strings in two ways, which I am sure you have seen something like this before:
1- toString function call: var x = 825; console.log(x.toString())
2- adding the number to a string: var x = '' + 825
so if you don't want numeric values, but only strings, just make sure you convert the value to string (even if it's a string nothing will happen) before you use it.
second, I don't think I really got your problem, but from what I got, your problem is much simpler that a regex, you're only replacing well defined string, so while iterating over the values all you need is:
var p = prop.toString();
if(p.startsWith("_p") {
p.replace("_p", "_c)
}
I hope this is what you are looking for

Understanding how javascript hashtables work

Could anyone explain to me why the code sample below reports true? I would have assumed that like in C# the instance of Test1 != instance of Test2.
Update: So I think I will go with some unique identifier stored in the base of both Test1 and Test2.
function Test1() { };
function Test2() { };
var test1 = new Test1();
var test2 = new Test2();
var dict = new Array();
dict[test1] = true;
alert(dict[test2]);
Your object (JavaScript's hashtable) is not using the instance of test1 or test2, but the string representation, as a key. Since both test1 and test2 have the same string representation: "[object Object]", the true value is associated with that key.
Try doing something like below instead:
function Test1(id) { this.id=id };
function Test2(id) { this.id=id };
var test1 = new Test1('1');
var test2 = new Test2('2');
var dict = {};
dict[test1.id] = true;
console.log(dict[test1.id]);
Keys in 'hashtables' (objects, basically) are always strings. So anything you add will be converted to a string.
new Test1();
returns a instance of Test1. Converted as a string, this is:
"[object Object]"
The same goes for Test2. So in fact, when storing true under the key of new Test1() as a string, you are working with the exact same record as the one by obtaining with the key new Test2() as a string. In other words,
(new Test1()).toString() == (new Test2()).toString();
The actual object is therefore simply:
{
"[object Object]": true
}
A solution is overwriting .toString() like this:
Test1.prototype.toString = function() { return "Test1" };
Test2.prototype.toString = function() { return "Test2" };
Then dict[test1] will be stored as dict['Test1'] and dict[test2] as dict['Test2'], which allows you to differ between them. Still, manually setting dict['Test1'] would overwrite things. As far as I know, there is no way to assign an object as a key.
Javascript objects aren't exactly hashtables; they're actually objects with string keys.
When you use an object as a key, the object is converted to a string by calling toString().
toString() will return the same string for all custom classes (unless you create your own toString), so they end up using the same key.
First: Use arrays only for numerical keys. For anything else use objects.
Property names can only be strings. Anything else is converted to its string representation. In case of objects, this is [object Object] or whatever toString() returns.
Which means, that if you want to make both objects distinguishable, you have to override this method and let it return something which is unique to each instance.
This question might help you: Hash/associative array using several objects as key

What is this javascript code doing?

this.String = {
Get : function (val) {
return function() {
return val;
}
}
};
What is the ':' doing?
this.String = {} specifies an object. Get is a property of that object. In javascript, object properties and their values are separated by a colon ':'.
So, per the example, you would call the function like this
this.String.Get('some string');
More examples:
var foo = {
bar : 'foobar',
other : {
a : 'wowza'
}
}
alert(foo.bar); //alerts 'foobar'
alert(foo.other.a) //alerts 'wowza'
Others have already explained what this code does. It creates an object (called this.String) that contains a single function (called Get). I'd like to explain when you could use this function.
This function can be useful in cases where you need a higher order function (that is a function that expects another function as its argument).
Say you have a function that does something to each element of an Array, lets call it map. You could use this function like so:
function inc (x)
{
return x + 1;
}
var arr = [1, 2, 3];
var newArr = arr.map(inc);
What the map function will do, is create a new array containing the values [2, 3, 4]. It will do this by calling the function inc with each element of the array.
Now, if you use this method a lot, you might continuously be calling map with all sorts of arguments:
arr.map(inc); // to increase each element
arr.map(even); // to create a list of booleans (even or odd)
arr.map(toString); // to create a list of strings
If for some reason you'd want to replace the entire array with the same string (but keeping the array of the same size), you could call it like so:
arr.map(this.String.Get("my String"));
This will create a new array of the same size as arr, but just containing the string "my String" over and over again.
Note that in some languages, this function is predefined and called const or constant (since it will always return the same value, each time you call it, no matter what its arguments are).
Now, if you think that this example isn't very useful, I would agree with you. But there are cases, when programming with higher order functions, when this technique is used.
For example, it can be useful if you have a tree you want to 'clear' of its values but keep the structure of the tree. You could do tree.map(this.String.Get("default value")) and get a whole new tree is created that has the exact same shape as the original, but none of its values.
It assigns an object that has a property "Get" to this.String. "Get" is assigned an anonymous function, which will return a function that just returns the argument that was given to the first returning function. Sounds strange, but here is how it can be used:
var ten = this.String["Get"](10)();
ten will then contain a 10. Instead, you could have written the equivalent
var ten = this.String.Get(10)();
// saving the returned function can have more use:
var generatingFunction = this.String.Get("something");
alert(generatingFunction()); // displays "something"
That is, : just assigns some value to a property.
This answer may be a bit superflous since Tom's is a good answer but just to boil it down and be complete:-
this.String = {};
Adds an object to the current object with the property name of String.
var fn = function(val) {
return function() { return(val); }
}
Returns a function from a closure which in turn returns the parameter used in creating the closure. Hence:-
var fnInner = fn("Hello World!");
alert(fnInner()); // Displays Hello World!
In combination then:-
this.String = { Get: function(val) {
return function() { return(val); }
}
Adds an object to the current object with the property name of String that has a method called Get that returns a function from a closure which in turn returns the parameter used in creating the closure.
var fnInner = this.String.Get("Yasso!");
alert(fnInner()); //displays Yasso!

Categories

Resources