I am developing a system that is based on a lot of filters. I get an input, and the input needs to be sanitised. We have already build a large system for sanitation, that has a lot of logic inside it, however we wish to simplify this and make it more agile.
A very simple example: A filter could be, that we get in a feed that contains the words "Boat" "Boaat" "BoAt" and "boat". They should all be mapped to the same entity "boat" in our database.
Now, what we has been looking for, is a way to run very limited javascript, lua, python, anything - inside PHP, without the use of PECL or new binaries for PHP. With this our editors could do something like, very conditional (in JS):
var r = VALUE.toLowerCase();
if (r=="boaat")r="boat";
return r;
These examples of filters above are -very- simple, and are just to simply what we are looking for.
I would like to keep out of the way of anything that renders down to a simple PHP eval.
So simple question: When wanting a simple logic/scripting-engine inside PHP, that can be executed, with the options to manually insert the "variable environment", what are my options?
Why not collect your words in an object and get the normalized word out of it with an access by a lower case word.
{
boat: 'boat',
boaat: 'boat'
}
Access
word = words[string.toLowerCase()];
Related
So i've been asked to remake some registration forms. The way its supposed to work is, that an interpreter chooses X amount of languages in the first select box. Then based on the selections of languages, the user must specify from which languages they can translate from/to.
I want to store this data in a key/value array, with the key being "LanguageFrom" and Value being another array, of "LanguagesTo". This is how i have solved this:
function btnTest() {
var fromArray = $('.freelancerLanguagesFrom').map(function() {
return $(this).val();
}).get();
var toArray = $('.freelancerLanguagesTo').map(function() {
return $(this).val();
}).get();
var tempArray = {};
tempArray[fromArray] = toArray;
}
This method is being called with an "onclick" function in the html part. The user should specify which languages he can translate to for each of the chosen languages in the first box,
I am aware that this probably isn't the ideal approach, but im still an inexperienced developer, and i'd love to hear your take on another approach.
Now comes my problem:
1) How do i make it so the array wont overwrite the existing array with each button click, and instead just add to the array?
2) How do i process this array on the server side (php), so that i can store the values in my database?
3) Is it possible to skip the flow where the user has to press the save(gem) button after each language he has chosen?
edit: Question 1 and 3 are now solved, my only problem is accessing the array i made in js, on the php side
1) tempArray exists only in the scope of the btnTest() function. Declare it outside (in the global scope), initialize it as {} and don't reset it every time you click the button. The way you get the fromArray variable may require some tweaking depending on whether the "from" list can accept a multiple selection or not.
2) Ajax may help. Create a php endpoint to receive the request and call it using ajax. You can work on the array using JSON. Send your data using JSON.stringify(tempArray) and read it using json_decode() in your php script, or simply set the request headers as "application/json" to have it done automatically for you.
3) I personally wouldn't automate this process. Let's say I have 4 languages, Italian, English, French and Chinese.
I have selected a desirable state of languages I can handle:
Italian -> English, French
But I also know how to translate French in Italian so I click, in the from list, French, and I get
French -> English
Which is an undesirable state, for me, because I don't know how to do that. Especially if I were to select many languages, I'd get, inbetween 2 states I want to save, an indefinite amount of states I don't want to save.
If you still want to do so, you need to move the even listener from the button to the list(s), with the onchange event.
I'd also suggest you do your event binding trough jQuery, if you aren't already.
Hope this helped.
I am surprised that no one on StackOverflow asked this question before.
Looking through the JSON object documentation and a quick google search did not yield satisfactory results.
What's the advantage of it? How does it work?
Edit: To make it clear, take a look at this flatten/un-flatten example.
Fastest way to flatten / un-flatten nested JSON objects
Thank you.
There are many situations where you get JSON text that was automatically built by some library. Throughout the programming languages, there are many libraries that build JSON text (one example is here).
Whenever libraries add some additional object or array wrappings, you might want to get rid of them maybe because you send the JSON to the server and your code there crashes because it expects a primitive value instead of an object (or an array). Or, if your JSON is a server response, you don't want the resulting Javascript code having to differ between object/array or not object/array. In all these cases, flattening is helpful as it will save you time. You will have to implement lesser if/elses, and you can reliably expect your data structure to be as flat as possible.
The other approach to improve code for the scenario mentioned is to write the code in a maximal robust way so there is no way for it to crash by superfluous wrappings ever. So always expect some wrappers and get it's contents. Then, flattening is not needed.
You see, it depends on what is building the JSON and what is parsing it. The building may be out of your scope.
This leads also to data model questions. I've worked with XML code that needed to be parsed quiet a different way if there where 0 entries of some XY, or if there were >0 entries of some XY. Having a wrapper that is allowed to have 0 or more entries of some XY will make live easier. These are data model desicions.
In all cases where the JSON represents an object structure that I've combined manually, I expect it not to change. So flattening something I've designed in detail would be disturbing. Standard operations as far I've seen them do not need flattening (e.g. JSON.stringify(), json_encode() etc.)
Here's a simple scenario: In a web app you have an HTTP POST that is updating a complex relational object.
POST
update=1
&user.id=12345
&user.email=testmail#domain.tld
&user.profile.name=Mr. Test
&user.profile.age=42
&user.profile.friend.0.email=tom#domain.tld
&user.profile.friend.1.email=sally#domain.tld
&user.profile.friend.2.email=bob#domain.tld
&user.profile.skill.0.id=100
&user.profile.skill.0.name=javascript
&user.profile.skill.1.id=200
&user.profile.skill.1.name=piano
Everything is already in a flat structure, so why not have a simple one-to-one binding? If you had a list of constraints or security requirements that you needed to enforce you could validate them by searching directly on the sorted key list.
Flat structures are easier for people to understand and work with there's even some cross-over with database de-normalisation. It also allows for context specific security and constraints to be implemented in a readable, but more verbose way.
When showing a user's view in full you may want to hide the display of the primary key ID for the user's list of skills.
"user.profile.skill.#.id": { hidden: true, readonly: true }
But when looking directly at a skill (to possibly edit it as an administrator) you may want to see the ID.
"skill.id": { readonly: true }
If you were writing a user-centric/self-service type CMS application you'd get more users on board and able to contribute using a straightforward flat model (flat abstraction of the underlying nested relational model) than you would with just the nested model.
TLDR: Flat is easier to read than nested. While programmers can handle nested schemas, recursive parsing and processing; end-users and admins usually prefer that part abstracted away.
I realize this is a 5 year old question at this point, but I figured, I'd add my thoughts to it, in case someone runs into a similar use case and finds this useful.
One of the use cases why you would want to flatten a JSON object, is for dynamic template binding via Regular Expression (RegEx) string interpolation. Well wasn't that a mouthful 👀😅? It simply translates to "template filling a string without hardcoding".
Ok Imagine a scenario, you have a template string like this for an email:
Hello {{firstName}},
It is amazing you chose to join our site. We are happy to have you on board.
To get started, we would really love it if you can confirm your email address
by clicking on the link: {{confirm_url}}.
Welcome aboard
The Team!
Given the following JSON object in memory:
{
"user" : {
"prefix" : "Dr.",
"firstName" : "Awah",
"lastName" : "Teh",
"email" : "awah#superduperubercoolsite.com",
"address" : {
"street": "100 Main St",
"city" : "PleasantVille",
"state" : "NY",
"phone" : "+1-212-555-1212"
}
},
"meta" : {
"confirm_url" : "http://superduperubercoolsite.com/confirm/ABC123"
}
}
it seems super simple to do a Regular Expression replace like so (assuming our email template string was stored in a variable named template and the json object was stored in a variable called templateData:
template = template.replace(new RegExp('{{firstName}}', 'g'), templateData.user.firstName);
template = template.replace(new RegExp('{{confirm_url}}', 'g'), templateData.meta.confirm_url);
Easy right? --> Actually yes! How about this email had 10 templated fields, or you wanted to decouple the template from the code, by storing it in a separate system like SendGrid, where your cool head of marketing can access the template and make changes to the copy-language, without having to call someone from engineering to make changes to the code, test the code and redeploy to production (what a hassle).
This is exactly where flattening of the JSON comes save the day!
Now there are many ways to flatten JSON, I have attached a link to a codepen I wrote that has logic to flatten JSON (actually, I demonstrate two similar but different approaches in the methods flattenJSONIntoKVP and flattenJSONIntoRAW check 'em out!).
That said, there are other implementations out there, and it is worth remembering that the focus on this post is to discuss the WHY JSON flattening could be useful, not the HOW.
Moving on! Assume you flattened the JSON from above (using my implementation that results in key value pairs) to something like this:
[
{ "key": "user.prefix", "value": "Dr."},
{ "key": "user.firstName", "value": "Awah"},
{ "key": "user.lastName", "value": "Teh"},
{ "key": "user.email", "value": "awah#superduperubercoolsite.com"},
{ "key": "user.address.street", "value": "100 Main St"},
{ "key": "user.address.city", "value": "{PleasantVille"},
{ "key": "user.address.state", "value": "NY"},
{ "key": "user.address.phone", "value": "+1-212-555-1212"},
{ "key": "meta.confirm_url", "value": "http://superduperubercoolsite.com/confirm/ABC123"},
]
Now, my friend, you are cooking with GAS!
Why, cause now you can dynamically interpolate the template string with values from the JSON object without giving too much worry to the structure of the JSON (if it changes due to the application evolving, you don't have to also remember to come down here and change this interpolation code -- you simply just have to update the email template itself, which mind you, is on SendGrid [per this example]).
So how to do it you say?: Simple, iteratively. Let's assume that flattened from above was stored in a variable called flatJSON:
///Notice how I use Javascripts native string interpolation to create my RegExp
///Also note that I am replacing the dot (.) in my flattened JSON variable names with a double underscore (__), I only do this because my intended target is SendGrid, and I don't believe it likes dots in its template placeholders.
flatJSON.forEach(kvp=>template = template.replace(new RegExp(`{{${kvp.key.replace(/\./g, '__'}}}`, 'g'), kvp.value));
That's it, one line of code to replace possibly 10 or even hundreds or even thousands (ok.. maybe not thousands, but you get the point).
Ohh! almost forgot, we need to update our template string.
Notice how now, in our new templated string we can use a somewhat FQDN style variable to map back to our original JSON (Ideally if SendGrid supported dots in their template placeholders, this would look super sweet but alas, can't always win everything!😭.
Hello {{user__firstName}},
It is amazing you chose to join our site. We are happy to have you on board.
To get started, we would really love it if you can confirm your email address
by clicking on the link: {{meta__confirm_url}}.
Welcome aboard {{user__prefix}} {{user__lastName}}!
The Team!
Et Voila!
Just like that, we have accomplished some good here today; we have:
Answered the WHY of flattening JSON objects
We dibble-dabbled into the how, with the codepen example
And we even overviewed a use case where taking advantage of JSON flattening can help you write durable dynamic code, that evolves as your underlying object structures change -- and that doesn't require you to leverage the big bad ugly eval method (we can talk about big bad ugly eval on another post).
Just playing around with a JSON array and wanted to know if it was possible to console log from directly within a JSON array. ie:
{ "id": "1", "type": "text", "description": "hello <script>console.log('console this text')</script> I am testing },
In the above example it will display the <script>console.log('console this text')</script> as text rather than as actual html. Any way to make this work to produce the message in console by placing it within the array?
That really depends on what you mean...
In straight JavaScript, you can use object notation in that way, but you really need to wrap it in a function call.
In addition to this, you are mixing javascript and loose text very badly... you really need to let the browser know which one you are using and when.
For example, those script tags? If you are already using JavaScript, then why tell the browser 'here is some script'?
With a little cleaning up:
var myObject = {
id: "1",
type: "text",
description: function(){console.log('console this text')}
}
myObject.description();
This is valid JavaScript and will run perfectly well in a browser if entered into the page this way.
However, I suspect that this isn't what you mean... what you intend to do is to pull this from an AJAX call, for example, and have it run arbitrary script within a browser.
That will not work.
JSON, used this way, is designed as a data format, and does not allow methods to be passed, only properties.
However, there are some uses where this type of behavior could be coaxed: LOOK HERE.
In short, ANY text, JSON or not, could be evaluated on a client system and could potentially run malicious code. This is very similar to security issues in PHP where poor programming practice allows the use eval and other exploits to run client fed code on the server.
This is why so many websites are neurotic in their scrubbing of any data which has been fed by an arbitrary user... scrubbing html tags and javascript code out of user comments, for example.
You can try using a self invoking function.
var obj = {
'a' : '1',
'b' : '2',
'c' : (function(){console.log('3')})()
}
I have a dynamically created list, but each element is a pretty big HTML chunk that has a common structure and a few variables.
What would be the best way to do it with jQuery?
One way to handle large amounts of HTML in the client is to use atemplate system. If you want to keep it jQuery centric then jQuery templates are one option but I believe it has been dis-continued.
I've had a lot of success with Underscore templates, which are fast and easy to use.
Not sure if this is overkill for you, but maybe check out a templating library.
https://github.com/janl/mustache.js is really good I've heard (personally haven't used it).
I've used the one from Underscore.js
The latter would look something like this..
var myTemplate = _.template("<div>This is my reusable block with {{ count }} variables");
$.each([1,2,3,4], function(elm, i){
$('body').append(myTemplate ({count: elm}));
})
Here's a jsfiddle example http://jsfiddle.net/K8VHb/
I'll echo what CambridgeMike and Simon Smith said, "Use a template library," except that I'll plug a different and I think better one. Use Handlebars.js. It's the same one Ember.js picked, it's great standalone and it pairs well with Backbone.js as well.
It's something you can use for a long time.
Taking advantage of the join method of arrays (concatenates elements into strings) is my favorite approach. Using this along with JQ, I've never really understood the appeal of JS templating libraries. It's also convenient for formatting HTML in an easy-to-read style without sweating line-breaks in JavaScript. I didn't test this so there may be syntax goofs.
var standardList = [
'<li>Always here</li><!--could add a bunch more non-variable LIs here-->',
'<li>',
, //this is key #2 - one comma per line at the end makes counting easy
'</li>',
'<li>More non-variable LIs</li><!--possibly more non-variable LIs here-->',
'<li>',
, //this is key #6
'</li>'
];
//you could have lots of these or an array of them dropped in from a server
var variableListItems = {
key_2:'first Var list value',
key_6:'second variable list value'
};
function buildFromListTemplate(valuesObj,listTemplate){
for(var x in valuesObj){
listTemplate[x.split('_')[1]] = valuesObj[x];
//x.split('_') returns array ['key',<number>] for each key in object
//so [1] gives array key
}
return listArray.join(''); //smooshes array into one big string
}
$('#someExistingUL').html(
buildFromListTemplate(variableListItems, standardList)
);
Note: A powerful but easy upgrade would be to handle the LI-wrapping of values inside the buildFromListTemplate function. That way, the keys you put inside your values object would all be optional and wouldn't leave a blank LI when not provided.
When it comes to formatting big hunks of HTML, split/join are your friend and have powerful utility in general. Another handy use is to build a list or table row from an existing set of values:
var ulHtmlString = [
'<ul><li>',
arrayOfValues.join('</li><li>'),
'</li></ul>'
].join('')
You can build on this to auto-convert 2D arrays into tables as well. Learn some regEx (which can be used in place of strings in the split method) and you can easily convert back from HTML to data-only arrays and objects in cases where the HTML isn't uniform.
i would use jQuery templates.
using jquery templates consists of two pieces:
your template (usually HTML):
<script id="myTemplate" type="text/x-jquery-tmpl">
<div>${text} ${otherStuff}</div>
</script>
and your data (usually JSON or a javascript object):
var data = { text: 'hello world!', otherStuff: 'foobar' };
Then you just bind the data to the template
var resultingHTML = $('#myTemplate').tmpl(data);
then append the resulting HTML to your DOM:
$('#someContainer').append(resultingHTML);
jQuery templates offer a lot of flexibility with conditionals/loops in your templates such as {{if}} and {{each}}, and JSON is a very easy data transfer language to work with.
I need to implement a simple way to handle localization about weekdays' names, and I came up with the following structure:
var weekdaysLegend=new Array(
{'it-it':'Lunedì', 'en-us':'Monday'},
{'it-it':'Martedì', 'en-us':'Tuesday'},
{'it-it':'Mercoledì', 'en-us':'Wednesday'},
{'it-it':'Giovedì', 'en-us':'Thursday'},
{'it-it':'Venerdì', 'en-us':'Friday'},
{'it-it':'Sabato', 'en-us':'Saturday'},
{'it-it':'Domenica', 'en-us':'Sunday'}
);
I know I could implement something like an associative array (given the fact that I know that javascript does not provide associative arrays but objects with similar structure), but i need to iterate through the array using numeric indexes instead of labels.
So, I would like to handle this in a for cycle with particular values (like j-1 or indexes like that).
Is my structure correct? Provided a variable "lang" as one of the value between "it-it" or "en-us", I tried to print weekdaysLegend[j-1][lang] (or weekdaysLegend[j-1].lang, I think I tried everything!) but the results is [object Object]. Obviously I'm missing something..
Any idea?
The structure looks fine. You should be able to access values by:
weekdaysLegend[0]["en-us"]; // returns Monday
Of course this will also work for values in variables such as:
weekdaysLegend[i][lang];
for (var i = 0; i < weekdaysLegend.length; i++) {
alert(weekdaysLegend[i]["en-us"]);
}
This will alert the days of the week.
Sounds like you're doing everything correctly and the structure works for me as well.
Just a small note (I see the answer is already marked) as I am currently designing on a large application where I want to put locals into a javascript array.
Assumption: 1000 words x4 languages generates 'xx-xx' + the word itself...
Thats 1000 rows pr. language + the same 7 chars used for language alone = wasted bandwitdh...
the client/browser will have to PARSE THEM ALL before it can do any lookup in the arrays at all.
here is my approach:
Why not generate the javascript for one language at a time, if the user selects another language, just respond(send) the right javascript to the browser to include?
Either store a separate javascript with large array for each language OR use the language as parametre to the server-side script aka:
If the language file changes a lot or you need to minimize it per user/module, then its quite archivable with this approach as you can just add an extra parametre for "which part/module" to generate or a timestamp so the cache of the javascript file will work until changes occures.
if the dynamic approach is too performance heavy for the webserver, then publish/generate the files everytime there is a change/added a new locale - all you'll need is the "language linker" check in the top of the page, to check which language file to server the browser.
Conclusion
This approach will remove the overhead of a LOT of repeating "language" ID's if the locales list grows large.
You have to access an index from the array, and then a value by specifying a key from the object.
This works just fine for me: http://jsfiddle.net/98Sda/.
var day = 2;
var lang = 'en-us';
var weekdaysLegend = [
{'it-it':'Lunedì', 'en-us':'Monday'},
{'it-it':'Martedì', 'en-us':'Tuesday'},
{'it-it':'Mercoledì', 'en-us':'Wednesday'},
{'it-it':'Giovedì', 'en-us':'Thursday'},
{'it-it':'Venerdì', 'en-us':'Friday'},
{'it-it':'Sabato', 'en-us':'Saturday'},
{'it-it':'Domenica', 'en-us':'Sunday'}
];
alert(weekdaysLegend[day][lang]);