Use Function template technology to write a simple and efficient JSON queryer

Source: Internet
Author: User

JSON is a highlight of JavaScript. It can use elegant and concise code to initialize objects and arrays. It is also a text-based data definition, which is more semantic than symbol separation and more concise than XML. Therefore, more and more JS developers are using it for data transmission and storage.

  

JS arrays have built-in many useful methods to facilitate data query and filtering. For example, we have a bunch of data:

VaR heros = [// name = ==== intelligence ==={ Name: 'ice room witch ', DP: 38, AP: 1.3, STR: 16, AGI: 16, INT: 21}, {Name: 'silent locker', DP: 39, AP: 1.1, STR: 17, AGI: 16, INT: 21}, {Name: 'nagarhaid', DP: 51, AP: 6.0, STR: 21, AGI: 21, INT: 18}, {Name: 'bounty hunter ', DP: 39, AP: 4.0, STR: 17, AGI: 21, INT: 16}, {Name: 'highly toxic wareer', DP: 45, AP: 3.1, STR: 18, AGI: 22, INT: 15 },{ name: 'guard of light ', DP: 38, AP: 1.1, STR: 16, AGI: 15, INT: 22}, {Name: 'authorization', DP: 49, AP: 0.6, STR: 25, AGI: 11, INT: 25} //...];

  

To query heroes whose attacks are greater than 40 and whose defenses are less than 4, we can use the filter method of array:

    var match = heros.filter(function(e) {        return e.DP > 40 && e.AP < 4;    });

  
Returns an array containing two results that meet the conditions.
  
Compared with manual write loop judgment, the filter method provides us with great convenience. However, it is based on function callback. Therefore, you must write a function every time you use it. This is cumbersome for simple queries, and the callback efficiency is greatly reduced. However, there is no way to do this. To be simple, you must sacrifice certain performance. If you can useThis is a simpler statement and has a full code expansion validity rate., How perfect it should be.
  
  First, you can imagine that you can write the above Code as follows, and the query speed is the same as that of handwritten traversal:

    var match = heros.select('@DP>40 AND @AP<4');

 

It looks a bit like SQL, and even the syntax has changed? In this case, I don't want to write a lot of script engine functions such as lexical analysis, semantic interpretation, and so on. Even a few thousands of lines of code are not fixed, and the efficiency is definitely worse... If the thought is so complicated, you have not understood the essence of the script. However, all scripting languages have interfaces for dynamically interpreting code during runtime, such as execute () of vbs, Eval (), new function () of JS (), you can even create a <SCRIPT> to write code dynamically.
  
  Obviously, if another language can be translated into JS Code, it can be directly handed over to the host for execution!
  
For example, replace "@" with "E. "," and "is replaced with" & ", so it becomes a valid JS expression and can be executed by eval.
  
So what we need to do is to translate the original statement into a JS statement for execution. In addition, to improve efficiency, the translated JS expressionsInlineTo a Context Environment, generate an executable function body, instead of relying on callback to judge each traversal.
  
Therefore, the function template will be used.

 

Function template Introduction

In C ++, there is such a macro and class template that can complete some calculations in the compilation phase, greatly improving the performance of code in the runtime era. Although the script is not compiled in a strict sense, it will be parsed and fully optimized during the first execution. This is the competition between mainstream browsers. Therefore, we need to embed the code that repeats eval into the model function provided in advance: a function that is ready for negative expression calculation:

/*** Template: tmplcount * function: count the number of $ express expressions in the ARR array */function tmplcount (ARR) {var COUNT = 0; For (VAR I = 0; I <arr. length; I ++) {var E = arr [I]; if ($ express) {count ++ ;}} return count ;}

  

The above is a template function, traversing ParametersArr []And statistical compliance$ Express. BesidesIf (...)All the other expressions are ready. Character$ ExpressYou can also replace it with another identifier, as long as it does not conflict with other characters in the function.
  
When we need to instantiateTmplcount. tostring ()Convert the function to the string format, and then convert$ ExpressReplace it with the expression we want, and getFunction TypeA template function instance is generated!

Below is a simple demonstration:

/*** Function: createinstance * parameter: EXP * a JS expression string used to replace $ express * in the tmplcount template. Return: * a function is returned, instance of the template tmplcount */function createinstance (exp) {// replace the expression var code = tmplcount in the template. tostring (). replace ('$ express', exp); // prevents the direct eval error of anonymous functions from VAR fn = eval ('0, '+ code); // returns the template instance return FN ;} // test the parameter var student = [{Name: 'Jane ', age: 14}, {Name: 'jack', age: 20}, {Name: 'Adam ', age: 18}]; // demo1 var F1 = Createinstance ('E. age <16 '); alert (F1 (student); // One // demo2 var F2 = createinstance ('E. Name! = "Jack" & E. age> = 14'); alert (F2 (student); // 2

  

Note:Createinstance ()There is an object called E inTmplcountThe elements defined in the template refer to the time duration. ReturnedF1,F2YesTmplcountTwo instances of the template. The final calledF1,F2In the function, we have embedded our expression statements, just as we have written two functions with the same function in advance, so we can directly run the expressions during traversal without callback or anything, the efficiency is greatly improved.

  

To put it bluntly,TmplcountOnly to provide the string of this function, which is never called. In fact, it is also defined in the form of strings, but it is more intuitive to use functions for testing.

  It is worth noting that if the script requires compression optimization later, the tmplcount template will never be involved.Otherwise, the corresponding "E." and "$ Express" may change.

Basic JSON query function

The usage and implementation of function templates are complete. Let's look back at the previous JSON query language. We only need to translate SQL-like statements into JS expressions and generate a function template instance. For the same statement, we can cache it to avoid translation every time.

First, we implement the queryer template:

VaR _ PROTO = object. prototype; // template: _ tmpl // parameter: $ C // description: record and return the Element Set matching $ C in the _ list object // VaR _ tmpl = function (_ list) {VaR _ ret = []; VaR _ I =-1; for (VaR _ k in _ list) {VaR _ e = _ list [_ k]; If (_ E & _ e! = _ PROTO [_ k]) {if ($ C) _ RET [++ _ I] = _ E;} return _ ret;}. tostring ();

  

Then begin to write the select method of the object:

/// Select method implementation // VaR _ cache ={}; _ PROTO. Select = function (exp) {If (! Exp) return []; var fn = _ cache [Exp]; try {If (! FN) {var code = _ interpret (exp); // interpreted expression code = _ tmpl. replace ('$ C', Code); // apply to the template fn = _ cache [Exp] = _ compile (CODE ); // instantiate function} return FN (this); // query the current object} catch (e) {return [];}

The _ cache table caches query statements. Duplicate queries can greatly improve the performance.

    function __compile() {        return eval('0,' + arguments[0]);    }

_ Compile is written in an empty function separately to have a clean Context Environment for eval.

_ Interpret is the top priority of the system. It is responsible for translating query statements into JS statements. Its implementation is insightful, but it is as simple as possible. Do not over-analyze the syntax.

View specific code: http://files.cnblogs.com/index-html/jsonselect.js
For demonstration purposes, only some basic functions are implemented currently. You can also add like, between, order by, and other common functions in the future.

  Demo

VaR heros = [// name = ==== intelligence ==={ Name: 'ice room witch ', DP: 38, AP: 1.3, STR: 16, AGI: 16, INT: 21}, {Name: 'silent locker', DP: 39, AP: 1.1, STR: 17, AGI: 16, INT: 21}, {Name: 'nagarhaid', DP: 51, AP: 6.0, STR: 21, AGI: 21, INT: 18}, {Name: 'bounty hunter ', DP: 39, AP: 4.0, STR: 17, AGI: 21, INT: 16}, {Name: 'highly toxic wareer', DP: 45, AP: 3.1, STR: 18, AGI: 22, INT: 15 },{ name: 'guard of light ', DP: 38, AP: 1.1, STR: 16, AGI: 15, INT: 22}, {Name: 'authorization', DP: 49, AP: 0.6, STR: 25, AGI: 11, INT: 25} //...];
   Test: Heros. Select (' ') Query

// Query: the strength and agility exceed 20. // result: Naga haidao var match = heros. select ('@ STR> 20 and @ AgI> 20'); // query: the URL at the end of "Shi". // result: silent locks, highly toxic locks, alchemist var match = heros. select ('Right (@ name, 1) = "Shi" '); // query: if the life value exceeds 500, // result: alchemist var match = heros. select ('1970 + @ Str * 19> 100 ');

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.