I'd like to do the following
var obj = {
animal: "${animal}"
};
var res = magic(obj, {animal: "cat"});
// res => {animal: "cat"}
magic is some function that does the dirty work. Obviously obj could be more complex with multiple keys, nested arrays and so on. The template variable could be inside an array like this
var obj = {
animals: ["cat", "dog", "${animal}", "cow"]
};
and it could be anywhere in the array so simply doing obj.animals[2] = "bat"; isn't feasible.
I've found the underscore-tpl library with which I can achieve what I want, but I would like to know if there are other solutions for future reference and because I had a hard time finding underscore-tpl in the first place.
My actual use is case is that I have a config.json file where I have several declarations like the following
{
"task1": {
"command": "command-line-program",
"args": [
"--input", "{{input}}",
"--flag1",
"--output", "{{output}}",
"--flag2",
],
"options": {
"cwd": "path-to-working-dir"
}
}
}
I parse this consig.json using JSON.parse(...) and I call require("child_process").spawn with the command, args and options parameters declared in the file, however args change a lot, flags added, reordered and stuff, so simply doing config.task1.args[1] = "<input value>"; involves changing the code that invokes spawn and this is as error prone as it gets.
Update
Based on the accepted answer I've created a simple package (located here) which I can include in my projects, feel free to use it.
You could JSON.stringify the object, then replace your search value with the actual value, then JSON.parse the result:
function magic(o, a) {
var j = JSON.stringify(o);
for (var k in a) {
j = j.split('${'+k+'}').join(a[k]);
}
return JSON.parse(j);
}
I suggest to you a very simple but very fast and understandable template engine:
simple-template.js
It consists of 22 lines of code, very simple!
Considering that, you will be able to render your config easily!
Related
I have a bizarre issue happening here. I am retrieving data from a remote source and I need to reformat it for better internal use
The data structure is like the following:
clubs = [
0: {
propA: "blah",
probB: "bar",
events: [
0: {
data1: "foo",
data2: "bar"
}
1: {
data1: "this",
data2: "that"
}
1: {
propA: "hello",
probB: "bye",
events [
0: { ...}
1: {...}
...
...
I am looping through clubs and I want to assign the values of each club into its own events as property clubInfo. When I do that though, clubInfo becomes and endless chain of itself. So I delete the new clubInfo.events - and then everything but the root club[0] gets wiped out. I hope I am being clear, its hard to explain.
If I do this:
for (var i=0; i<clubs.clubs.length;i++) {
var clubInfo = clubs.clubs[i] ;
var events = clubs.clubs[i].events ;
for (var j=0; j<events.length; j++) {
}
}
Then clubs becomes:
clubs [
0: events [
clubInfo [
0: events [
clubInfo [
0: events [
....and goes on seemingly forever
]
]
]
]
]
]
If I do:
for (var i=0; i<clubs.clubs.length;i++) {
var clubInfo = clubs.clubs[i] ;
delete clubInfo.events ;
var events = clubs.clubs[i].events ; // events is now empty
for (var j=0; j<events.length; j++) { // errors here because there is no length after the delete
}
}
Then all that remains of clubs is, in fact none of the other properties that have arrays events or others (several of them) are all gone.:
clubs [
0: {
propA: "blah",
probB: "bar"
}
]
Instead of delete, i have even tried just nulling clubInfo.events = null - but the same issue happens, everything gets wiped out.
Oh boy, you've snagged one of JavaScript's current, and most obvious flaws by effectively using this:
clubs[i].event[j].clubInfo = clubs[i];
You're creating an infinite reference - what do I mean by that? It's better displayed through an Array, if you'll oblige me:
let a=[]; a.push([a]);
This creates an infinite level array through self-reference, creating an incalculable depth. You see, though there's a 32(2^32-1) bit limit to an Array's length. This can be demonstrated easily:
Array(2**32); //Uncaught RangeError: Invalid array length
Presumably this was done to prevent browser memory from shorting but, strangely, there was never any consideration to the depth an array may contain. A minor side effect of this is that there is no depth property, but a major side effect is that there is no protection from an infinite self-referencing array.
Getting Around It
The best way to get around this type of situation is to construct a new Object and assign properties from the old Object to the new. You can think of this as cloning. To do this you can utilize the assign method:
Object.assign(constructor, **Array**/**Object**)
Example:
let a = []; a.push(Object.assign([], a));
Problem solved, right? uhhh... not quite Even though this can sometimes work, this still won't fix the issue of an Array or Object with more than shallow references. To get around that you have to use a combination of:
JSON.stringify(obj);
to break references
JSON.parse(JSON);
to remake your object, and
delete obj[deepReference];
deletion to stop any unforeseen issues with any superfluous data/references
None of this is ideal, but currently there is no way to completely separate all references inside of an object or array without recursive iteration.
To give you an example - In your case you're going to want to do something like this:
for (var i = 0; i < clubs.length; i++) {
var clubInfo = clubs[i];
var events = clubs[i].events;
for (var j = 0; j < events.length; j++) {
let jsonTranslation = Object.assign({}, clubs[i]);
delete jsonTranslation.events;
jsonTranslation = JSON.stringify(jsonTranslation);
clubs[i].events[j].clubInfo = JSON.parse(jsonTranslation);
}
}
let clubs = [{
propA: "blah",
probB: "bar",
events: [{
data1: "foo",
data2: "bar"
},
{
data1: "this",
data2: "that"
}
]
}];
for (var i = 0; i < clubs.length; i++) {
var clubInfo = clubs[i];
var events = clubs[i].events;
for (var j = 0; j < events.length; j++) {
let jsonTranslation = Object.assign({}, clubs[i]);
delete jsonTranslation.events;
jsonTranslation = JSON.stringify(jsonTranslation);
clubs[i].events[j].clubInfo = JSON.parse(jsonTranslation);
}
}
console.log(clubs);
Additional Info: Other watch outs
Similarly there are other issues in the language. A badly implemented Array constructor method. Array(n) returns an Array with n members. Why's that an issue? Everything in JavaScript that is declared and not instantiated is undefined except the members of a freshly constructed array. They return empty. The issue with that is this means they have no mappable values. Ergo, all those sweet new functional ES Array methods are useless until the Array is filled. As an example:
Array(3).map((m, i) => i);
This results in well... the same thing you started with — when clearly it should provide a numbered array from 0-2. This is not as big of a deal as an infinite reference because you can work around it like this:
Array(3).fill(0).map((m,i) => i);
But it's effectively a wasted method call to take care of a problem that should be handled within construction.
Lastly, the fill method — give it an object or an Array and it doesn't create n individual object members. It creates n references to a singular array or object and stuffs them into one array. At base logic, it sort of makes sense. As #FelixKling pointed out in the comments, it is fairly consistent with what you would expect, a.e. fill this array with this one thing. I still would debate a bit about it's functionality for two reasons.
In what situation would anyone need n references stored in an Array to the same place in memory? Probably never. How often do people need an Array of similarly constructed Objects? All the time.
When passing an Object Literal for instance ( .fill({a:1}) )I can see the logic of filling the Array with references, even if it may not be wholly intuitive. If passed a Constructor- I would contest that it might make more sense to instantiate each member individually.
So there are many nuances, and inconsistencies with JavaScript that require knowledge to work around — and sadly, infinite reference, is one of them - but on the plus side the only way to typically realize these issues exist is to run into them headlong, so be thankful it wasn't in production!
Hope this helps! Happy Coding!
Is there exist any kind of c# dictionary in JavaScript. I've got an app in angularjs that requests data from an MVC Web Api and once it gets, it makes some changes to it. So the data is an array of objects, which is stored in the MVC Web Api as a Dictionary of objects, but I convert it to list before passing it throug network.
If I convert the Dictionary directly to JSon I get something like:
array = [ {Id:"1", {Id:"1", Name:"Kevin Shields"}},
{Id:"2", {Id:"2", Name:"Natasha Romanoff"}}
];
Well the objects are a little more complex, but you've got now an idea. The problem is that this format is even harder to operate with (I've got alphabetical keys or ids). So is there any equivalent to a dictionary? It's quite simple to do thing like:
Object o = dictionary["1"];
So that's it, thank in advance.
You have two options really, although both essentially do the same thing, it may be worth reading a bit more here, which talks about associative arrays (dictionaries), if you wish to tailor the solution:
var dictionary = new Array();
dictionary['key'] = 'value'
Alternatively:
var dict = [];
dict.push({
key: 'key',
value: 'value'
});
Update
Since ES2015 you can use Map():
const dict = new Map();
dict.set('{propertyName}', {propertyValue});
I know this question is a bit older, but in ES2015 there is a new data structure called map that is much more similar to a dictionary that you would use in C#. So now you don't have to fake one as an object, or as an array.
The MDN covers it pretty well. ES2015 Map
Yes, it's called an object. Object have keys and values just like C# dictonaries. Keys are always strings.
In your case the object would look like this:
{
"1": {
"Id": 1,
"Name":" Kevin Shields"
},
"2": {
"Id": 2,
"Name": "Natasha Romanoff"
}
}
The default ASP.net serializer produces ugly JSON. A better alternative would be Json.NET.
My Example:
var dict = new Array();
// add a key named id with value 111
dict.id = 111;
//change value of id
dict.id = "blablabla";
//another way
// add a key named name with value "myName"
dict["name"] = "myName";
//and delete
delete dict.id;
delete dict["name"]
//another way
dict = {
id: 111,
"name": "myName"
};
//And also another way create associate array
var myMap = { key: [ value1, value2 ] };
I'm working on a Dashboard widget and I came across a code that looks like this:
var variableName = {
"SomeName":"someValue",
"someName": "another value",
};
That's pretty much a sum of what it looks like. My question is what is it, how does it work and what can I do with it? An example would be perfect. Thanks in advance!
That's an object literal. It's pretty much just like this:
var variableName = new Object();
variableName.SomeName = "someValue";
variableName.someName = "another value";
This is an example of an object literal.
It creates a normal object with two properties.
While it's called an object literal in JavaScript, it acts more like an enum in most languages.
var messageState = {
new: 0,
read: 1,
deleted: 2
};
With that in place, you have an easy to read way of determining message state:
var message = GetMessage();
if (message.state == messageState.deleted) {
alert('This message is deleted');
}
This is also an easy way to organize functional pieces within your JS file. If you want to only use one JS file for you entire site, which is highly recommended for all kinds of optimization, you can use this instead of writing several different functions:
var Message {
sendMessage: function(msg) {
// method to send msg
},
deleteMessage: function(msg) {
// method to delete msg
}
};
var Vote = {
votePostUp: function(post) {
// method to vote post up
},
votePostDown: function(post) {
// method to vote post down
}
};
And to call:
Message.sendMessage(theMessage);
Vote.votePostUp(myPost);
This is a JavaScript object created with object literal notation.
You can access its properties like this:
variableName["SomeName"];
or
variableName.SomeName;
You can also iterate over the properties of said object (in arbitrary order) with a for...in loop:
for(var prop in variableName) {
alert(prop + " = " + variableName[prop]);
}
For an excellent guide of working with JavaScript objects, check out this MDN article on working with objects.
What you are looking at is an associative array (a hash map or a dictionary in some languages). Basically it's an array that associates an object with another object, like a word is an associated with its definition in a real dictionary.
It's also JavaScript's main form of objects (they associate function names with function bodies).
I am kind of new to the world of interface, and i found JSON is amazing, so simple and easy to use.
But using JS to handle it is pain !, there is no simple and direct way to push a value, check if it exists, search, .... nothing !
and i cannot simply add a one single value to the json array, i have this :
loadedRecords = {}
i want to do this :
loadedRecords.push('654654')
loadedRecords.push('11')
loadedRecords.push('3333')
Why this is so hard ???!!!
Because that's an object, not an array.
You want this:
var = loadedRecords = []
loadedRecords.push('1234');
Now to your points about JSON in JS:
there is no simple and direct way to push a value
JSON is a data exchange format, if you are changing the data, then you will be dealing with native JS objects and arrays. And native JS objects have all kinds of ways to push values and manipulate themeselves.
check if it exists
This is easy. if (data.someKey) { doStuff() } will check for existence of a key.
search
Again JSON decodes to arrays and objects, so you can walk the tree and find things like you could with any data structure.
nothing
Everything. JSON just translates into native data structures for whatever language you are using. At the end of the day you have objects (or hashes/disctionaries), and arrays which hold numbers strings and booleans. This simplicity is why JSON is awesome. The "features" you seek are not part of JSON. They are part of the language you are using to parse JSON.
Well .push is an array function.
You can add an array to ur object if you want:
loadedRecords = { recs: [] };
loadedRecords.recs.push('654654');
loadedRecords.recs.push('11');
loadedRecords.recs.push('3333');
Which will result in:
loadedRecords = { recs: ['654654', '11', '3333'] };
{} is not an array is an object literal, use loadedRecords = []; instead.
If you want to push to an array, you need to create an array, not an object. Try:
loadedRecords = [] //note... square brackets
loadedRecords.push('654654')
loadedRecords.push('11')
loadedRecords.push('3333')
You can only push things on to an array, not a JSON object. Arrays are enclosed in square brackets:
var test = ['i','am','an','array'];
What you want to do is add new items to the object using setters:
var test = { };
test.sample = 'asdf';
test.value = 1245;
Now if you use a tool like FireBug to inspect this object, you can see it looks like:
test {
sample = 'asdf,
value = 1245
}
Simple way to push variable in JS for JSON format
var city="Mangalore";
var username="somename"
var dataVar = {"user": 0,
"location": {
"state": "Karnataka",
"country": "India",
},
}
if (city) {
dataVar['location']['city'] = city;
}
if (username) {
dataVar['username'] = username;
}
Whats wrong with:
var loadedRecords = [ '654654', '11', '333' ];
Is there a more succinct way to write this?
var me = {};
for (var i in you) {
me[i] = you[i];
}
(where you is an arbitrarily-lengthed JavaScript array)
In other words, given the input of:
var you = [
"what",
"r",
"u"
];
The output, me, becomes:
me = {
0: "what",
1: "r",
2: "u"
};
Like, a one-liner that uses some functional method?
Why do you want to do this? Arrays in JavaScript are Objects, except with some additional properties like slice, splice, push and length.
Internally, Arrays and Objects are stored exactly in the same way: e.g. array[0] is the same as array["0"], or object["0"] (unlike in other languages where adjacent array indices are in fact in adjacent memory - array "indices" are simply converted into strings in JavaScript).
So, if you just want to copy the data, then this will suffice:
me = you.slice(); // me is a copy of you, but is still an array
Or, if you really want some sort of mapping functionality, then underscore.js provides a whole collection of functional programming tools for your perusal.
There is no built-in function that does what you ask, however some of the widely used javascript libraries like jQuery provide such a function. In jQuery's case: jQuery.extend()
Usage:
var me = {};
jQuery.extend(me,someObject);
//or, equivalently -
var me2 = jQuery.extend({},someObject);
jQuery has an extend() function (documentation here). Your code would look like this:
var me = {};
var you = ["what", "r", "u"];
$.extend(me, you);
This would allow you to do things like:
alert("Second element: " + me[1]);
It's a little odd, but I think it's what you're looking for.
I saw what you were trying to achieve with your string formatter. Instead of answering your original question of coming up with a concise implementation of one portion of it, I'll suggest a concise (and more flexible) implementation for the whole thing:
String.prototype.format = function () {
var args = arguments;
return this.replace(/\{(?:(\d+)|(\w+))\}/g, function (s, idx, prop) {
return prop && args[0]
? args[0][prop]
: args[idx];
});
};
When you have a number n inside a token "{n}", it uses the n-th argument for replacement. Otherwise, for non-numerical keys, it picks the corresponding property of the first argument.
For example:
"I have {1} {name}s in my basket.".replace({ type: "fruit", name: "eggplant" }, 4);
Returns:
"I have 4 eggplants in my basket."
The underscore.js library also has a basic extend function.
var me = _({}).extend(you)
or
var me = {}
_(me).extend(you)
or
var me = {}
_.extend(me, you)