javascript: array of object for simple localization - javascript

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]);

Related

How can a linq query be converted to JavaScript array containing only values?

I have a JavaScript grid library (it creates a table on the page) that accepts a JavaScript array as input, for rendering in the grid. I'm not certain, however, how to convert a Linq-to-SQL query (against a SQL Server database) to a JavaScript array containing only values.
I tried this, but it included the table column names in the JSON key (and I don't want JSON anyway, I want a JavaScript string array, unless this can be converted to an array?):
JsonConvert.SerializeObject(query)
Example of the format I need to produce:
[1,2,3],[4,5,6]
Environment: .NET Core 3.1
edit: Here is a sample of what I've currently got, this returns the less than desirable JSON (due to the query results being so large, having a JSON key for very element is going to literally double the size of the query):
Devices Table
ID Name
1 iPhone7
2 iPhone8
3 iPhone9
Needed Array (Note: no column names)
[1, "iPhone7"],[2, "iPhone8"],[3, "iPhone9"]
Current C# code in the controller method (returns undesirable key for every element currently)
var query = db.Devices;
var formattedResult = JsonConvert.SerializeObject(query);
return Ok(formattedResult);
Technically, you could do this:
var query = db.Devices.AsEnumerable()
.Select(d => new object[]{d.ID, d.Name});
var formattedResult = JsonConvert.SerializeObject(query);
return Ok(formattedResult);
But then the code on the other end of your request is going to have to translate all those arrays back into objects.
It's rarely worthwhile to complicate your model like this in order to optimize the size of your network traffic. If you're pulling enough items over the wire to make this a performance issue, you're likely to encounter a variety of other performance issues. I would first consider other options, like implementing paging.
Did you try
var query = db.Devices.ToList();
var array = JArray.FromObject(query);
return Ok(formattedResult)

Convert string to name of object (casting)

I want to cast a string to an existing object.
Background: I am using only JS, no libraries and no server side code. I have existing objects with elements I wish to display. I pass the name of the object using a query string so it arrives as a string. Example ?Room=Cube and what I wind up with is
nextRoom = getQueryString(); // which returns a string
and I want to display the Cube object. However nextRoom contains "Cube" and JS is not helpful, if I call a display function:
display(nextRoom)
Javascript treats it as a string and fails. Currently I fake it out with the object:
castToObj{"Cube":Cube, "Other":Other, "Etc":Etc, ........}
.....
room = castToObj[nextRoom]; // accessing the object returns the room Object
then I can display the room by calling:
display(room); // now JS treats the parameter as an object
But this requires me to rewrite code to modify the castToObj{} contents every time I add a room. I would like a way to turn "Cube" into Cube, in other words turn a string into an object. I have tried many variations but have been unsuccessful. I could list ways I have tried but that seems senseless because they were all failures.
HELP! PLEASE!
P.S I retired about twenty years ago before learning C++, OOP, etcetera so my javascript skills are just my "C" programming experience expanded.
First create an empty container object:
myElements = {};
Then change your code wherever you define an element, from
Cube = ... whatever ...
to
myElements.Cube = ... whatever ....
for all of your elements (Other, Etc ...).
After this, you simply use
nextRoom = getQueryString();
display(myElements[nextRoom]);
You can use java script eval function but depending on the situation it might open your program to XSS attack. You should use it carefully.

what is the best way to insert repeating HTML blocks with small changes?

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.

Lucene-like searching through JSON objects in JavaScript

I have a pretty big array of JSON objects (its a music library with properties like artist, album etc, feeding a jqgrid with loadonce=true) and I want to implement lucene-like (google-like) query through whole set - but locally, i.e. in the browser, without communication with web server. Are there any javascript frameworks that will help me?
Go through your records, to create a one time index by combining all search
able fields in a single string field called index.
Store these indexed records in an Array.
Partition the Array on index .. like all a's in one array and so on.
Use the javascript function indexOf() against the index to match the query entered by the user and find records from the partitioned Array.
That was the easy part but, it will support all simple queries in a very efficient manner because the index does not have to be re-created for every query and indexOf operation is very efficient. I have used it for searching up to 2000 records. I used a pre-sorted Array. Actually, that's how Gmail and yahoo mail work. They store your contacts on browser in a pre-sorted array with an index that allows you to see the contact names as you type.
This also gives you a base to build on. Now you can write an advanced query parsing logic on top of it. For example, to support a few simple conditional keywords like - AND OR NOT, will take about 20-30 lines of custom JavaScript code. Or you can find a JS library that will do the parsing for you the way Lucene does.
For a reference implementation of above logic, take a look at how ZmContactList.js sorts and searches the contacts for autocomplete.
You might want to check FullProof, it does exactly that:
https://github.com/reyesr/fullproof
Have you tried CouchDB?
Edit:
How about something along these lines (also see http://jsfiddle.net/7tV3A/1/):
var filtered_collection = [];
var query = 'foo';
$.each(collection, function(i,e){
$.each(e, function(ii, el){
if (el == query) {
filtered_collection.push(e);
}
});
});
The (el == query) part of course could/should be modified to allow more flexible search patterns than exact match.

jQuery "Autocomplete" plugin is messing up the order of my data

I'm using Jorn Zaefferer's Autocomplete plugin on a couple of different pages. In both instances, the order of displayed strings is a little bit messed up.
Example 1: array of strings: basically they are in alphabetical order except for General Knowledge which has been pushed to the top:
General Knowledge,Art and Design,Business Studies,Citizenship,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Displayed strings:
General Knowledge,Geography,Art and Design,Business Studies,Citizenship,Design and Technology,English,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Note that Geography has been pushed to be the second item, after General Knowledge. The rest are all fine.
Example 2: array of strings: as above but with Cross-curricular instead of General Knowledge.
Cross-curricular,Art and Design,Business Studies,Citizenship,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Displayed strings:
Cross-curricular,Citizenship,Art and Design,Business Studies,Design and Technology,English,Geography,History,ICT,Mathematics,MFL French,MFL German,MFL Spanish,Music,Physical Education,PSHE,Religious Education,Science,Something Else
Here, Citizenship has been pushed to the number 2 position.
I've experimented a little, and it seems like there's a bug saying "put things that start with the same letter as the first item after the first item and leave the rest alone". Kind of mystifying. I've tried a bit of debugging by triggering alerts inside the autocomplete plugin code but everywhere i can see, it's using the correct order. it seems to be just when its rendered out that it goes wrong.
Any ideas anyone?
max
EDIT - reply to Clint
Thanks for pointing me at the relevant bit of code btw. To make diagnosis simpler i changed the array of values to ["carrot", "apple", "cherry"], which autocomplete re-orders to ["carrot", "cherry", "apple"].
Here's the array that it generates for stMatchSets:
stMatchSets = ({'':[#1={value:"carrot", data:["carrot"], result:"carrot"}, #3={value:"apple", data:["apple"], result:"apple"}, #2={value:"cherry", data:["cherry"], result:"cherry"}], c:[#1#, #2#], a:[#3#]})
So, it's collecting the first letters together into a map, which makes sense as a first-pass matching strategy. What i'd like it to do though, is to use the given array of values, rather than the map, when it comes to populating the displayed list. I can't quite get my head around what's going on with the cache inside the guts of the code (i'm not very experienced with javascript).
SOLVED - i fixed this by hacking the javascript in the plugin.
On line 549 (or 565) we return a variable csub which is an object holding the matching data. Before it's returned, I reorder this so that the order matches the original array of value we were given, ie that we used to build the index in the first place, which i had put into another variable:
csub = csub.sort(function(a,b){ return originalData.indexOf(a.value) > originalData.indexOf(b.value); })
hacky but it works. Personally i think that this behaviour (possibly coded more cleanly) should be the default behaviour of the plugin: ie, the order of results should match the original passed array of possible values. That way the user can sort their array alphabetically if they want (which is trivial) to get the results in alphabetical order, or they can preserve their own 'custom' order.
What I did instead of your solution was to add
if (!q && data[q]){return data[q];}
just above
var csub = [];
found in line ~535.
What this does, if I understood correctly, is to fetch the cached data for when the input is empty, specified in line ~472: stMatchSets[""] = []. Assuming that the cached data for when the input is empty are the first data you provided to begin with, then its all good.
I'm not sure about this autocomplete plugin in particular, but are you sure it's not just trying to give you the best match possible? My autocomplete plugin does some heuristics and does reordering of that nature.
Which brings me to my other answer: there are a million jQuery autocomplete plugins out there. If this one doesn't satisfy you, I'm sure there is another that will.
edit:
In fact, I'm completely certain that's what it's doing. Take a look around line 474:
// loop through the array and create a lookup structure
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
/* some code */
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[firstChar] )
and so on. So, it's a feature.

Categories