Witam,
napisałem niewielką bibliotekę, która pomaga wyszukiwać w tablicy obiekty spełniające określone kryteria.
Oto kod:
[JAVASCRIPT] pobierz, plaintext
  1. function JAQ(){
  2.  
  3. /*
  4. * Obiekt zawierający listę operatorów. Pierwszy element każdego operatora
  5. * zawiera funkcję zwracającą funkcję, która określa wartość logiczną wyrażenia dla konkretnego
  6. * elementu przesukiwanej tablicy.
  7. * Drugi element, to funckja generująca odpowiednią metodę obiektu klasy JAQ.
  8. */
  9. var operators = {
  10. or:[function(expr1, expr2){
  11. return function(obj){
  12. return expr1(obj) || expr2(obj);};
  13. }, prepare_logical_operator],
  14.  
  15. and:[function(expr1, expr2){
  16. return function(obj){
  17. return expr1(obj) && expr2(obj);};
  18. }, prepare_logical_operator],
  19.  
  20. xor:[function(expr1, expr2){
  21. return function(obj){
  22. return (expr1(obj) || expr2(obj)) && !(expr1(obj) && expr2(obj));};
  23. }, prepare_logical_operator],
  24.  
  25. eq:[function(field, value){
  26. return function(obj){return obj[field] == value;};
  27. }, prepare_comparison_operator],
  28.  
  29. gt:[function(field, value){
  30. return function(obj){return obj[field] > value;};
  31. }, prepare_comparison_operator],
  32.  
  33. lt:[function(field, value){
  34. return function(obj){return obj[field] < value;};
  35. }, prepare_comparison_operator],
  36.  
  37. lteq:[function(field, value){
  38. return function(obj){return obj[field] <= value;};
  39. }, prepare_comparison_operator],
  40.  
  41. gteq:[function(field, value){
  42. return function(obj){return obj[field] >= value;};
  43. }, prepare_comparison_operator]
  44. };
  45.  
  46. /*
  47.   * Zwraca funkcje odpowiadające logicznym operatorom dwuargumentowym,
  48.   * które mogą być wywołane z zewnątrz obiektu.
  49.   * Przyjmuje funkcję z tablicy operators.
  50.   */
  51. function prepare_logical_operator(func){
  52. /*
  53.   *
  54.   */
  55. function template_func(){
  56. if( this.allowed_operators == 'cmp' ){
  57. throw 'Incorrect query. Logical operator can be called after comparison or not operators only.';
  58. }
  59. this.logical_operator = func;
  60. this.allowed_operators = 'cmp';
  61. return this;
  62. }
  63. return template_func;
  64. }
  65.  
  66. /*
  67.   * Analogicznie do metody prepare_logical_operator,
  68.   * ale generująca metody dla operatorów porównania.
  69.   */
  70. function prepare_comparison_operator(func){
  71.  
  72. function template_func(field, value){
  73. /*
  74.   * Poniższa instrukcja przypisuje do zmiennej cnd
  75.   * funkcję powiązaną ze zmiennymi field i value.
  76.   * Zwrócona funkcja przyjmuje jako argument element listy, w której wyszukujemy,
  77.   * a zwraca wartość logiczną określającą, czy warunek jest dla tego elementu spełniony.
  78.   */
  79. var cnd = func(field, value);
  80.  
  81. //Jeżeli wcześniej wywołaliśmy not() neguje wyrażenie
  82. var condition = this.negate_condition
  83. && function(obj){return !cnd(obj);}
  84. || cnd;
  85.  
  86. this.negate_condition = false;
  87. /*
  88.   * W tym miejscu generowana jest funkcja query sprawdzająca, czy element tablicy
  89.   * przekazany jej w parametrze spełnia kryteria.
  90.   */
  91. this.query = this.logical_operator
  92. && this.logical_operator(this.query, condition)
  93. || condition;
  94.  
  95. this.allowed_operators = 'all';
  96. this.logical_operator = this.logical_operator || operators['and'][0];
  97. return this;
  98. }
  99. return template_func;
  100. }
  101.  
  102. this.not = function(){
  103. this.negate_condition = !this.negate_condition;
  104. this.allowed_operators = 'cmp';
  105. return this;
  106. };
  107.  
  108. this.each = function(func){
  109. for(i in this.data_set){
  110. func.call(this, this.data_set[i], i);
  111. }
  112. };
  113.  
  114. this.select=function(){
  115. this.data_result = [];
  116. this.each(function(obj){
  117. /*
  118.   * Wyszukanie elementów tablicy spełniających wymagane kryteria
  119.   * polega na wywołaniu funkcji query zwracającej true lub false.
  120.   * W przypadku spełnienia warunku
  121.   * odpowiedni element jest przypisywany do tablicy data_result.
  122.   */
  123. if(this.query(obj)){
  124. this.data_result.push(obj);
  125. }
  126. });
  127. return this.data_result;
  128. };
  129.  
  130. this.from = function(data){
  131. this.data_set = data;
  132. this.allowed_operators = 'cmp';
  133. this.logical_operator = null;
  134. this.negate_condition = false;
  135. this.query = null;
  136. return this;
  137. };
  138. /*
  139.   * Poniższy kod wywołuje funkcję generującą odpowiednie metody z tablicy operators
  140.   * tak ,aby były widoczne w przestrzeni nazw obiektu JAQ.
  141.   */
  142. for(func in operators){
  143. this[func] = operators[func][1](operators[func][0]);
  144. }
  145.  
  146. }
[JAVASCRIPT] pobierz, plaintext


Przykład:
[JAVASCRIPT] pobierz, plaintext
  1. var workers = [{"id":1,"name":"Alicja","age":23,"salary":3000},
  2. {"id":2,"name":"Bernadeta","age":25,"salary":2500},
  3. {"id":3,"name":"Drogomir","age":17,"salary":900},
  4. {"id":4,"name":"Kajetan","age":43,"salary":3000},
  5. {"id":5,"name":"Alojzy","age":31,"salary":7000},
  6. {"id":6,"name":"Alfred", "age":34,"salary":6000}];
  7. jaq = new JAQ();
  8. r = jaq.from(workers).lt('age', 27).and().not().gt('salary',2700).select();
[JAVASCRIPT] pobierz, plaintext

lt znaczy mniejsze niż, a gt większe niż. Cała instrukcja zwróci tych pracowników, którzy mają mniej niż 27 lat i nie większe wynagrodzenie niż 2700.
Efekt (w konsoli Firebuga):
>>> r
[Object { id=2, name="Bernadeta", more...}, Object { id=3, name="Drogomir", more...}]

Jak to działa?
W funkcji from przypisujemy do zmiennej data_set przeszukiwany zbiór, na niej będziemy dalej operować.
W tym miejscu ważne jest też, że każda metoda poza select zwraca obiekt this, żeby można było zapisać instrukcje np. w taki sposób:
jaq.from(workers).lt('age', 27).and().not().gt('salary',2700).[tu kolejne warunki do spełnienia].select().

Przejdźmy do instrukcji select.
Upewniamy się, że tablica przechowująca obiekty spełniające kryteria jest pusta.
Następnie wywołujemy funkję each, która wykonuje kod podany w parametrze na każdym elemencie tabeli data_set.
Ten kod dodaje dany element do tablicy wyników, jeśli funkcja query zwraca true.

Funkcja query w naszym przypadku jest równoważna takiej:
[JAVASCRIPT] pobierz, plaintext
  1. query=function(obj){
  2. return (function(obj){return obj["age"] < 27;})(obj)
  3. && (function(obj){
  4. return !(function(obj){return obj["salary"] > 2700;})(obj);
  5. })(obj);
  6. };
[JAVASCRIPT] pobierz, plaintext

Już piszę dlaczego.
Funkcja query jest generowana przez te metody, które są wywoływane pomiędzy from a select. Są to metody odpowiadające operatorom logicznym i porównania.
Są generowane w pętli w 142 linijce kodu na podstawie danych z tablicy operators. Ta tablica zawiera tylko podstawową logikę tych operatorów. Dostosowaniem ich do wykorzystania w zewnętrznych aplikacjach zajmują się metody prepare_logical_operator i prepare_comparison_operator. Zrobiłem to w taki sposób, gdyż dodawanie kolejnych operatorów jest banalnie proste niezależnie od logiki tej części biblioteki, która zbiera to wszystko jak mamy porównywać dane do kupy.
Opiszę dalej generowanie przykładowej metody jaką jest lt. Więc kiedy pętla z 142 linijki dojdzie do elementu tabeli operators o indeksie 'lt', wywoływana jest funkcja prepare_comparison_operator (drugi element tablicy przypisanej do elementu lt) z parametrem jakim jest pierwszy element tej tablicy.
Funkcja prepare_comparison_operator zwraca funkcję, która w tym wypadku na miejscu func (linijka 79) podstawi ten kod:
[JAVASCRIPT] pobierz, plaintext
  1. function(field, value){
  2. return function(obj){return obj[field] < value;};
  3. }
[JAVASCRIPT] pobierz, plaintext

Łatwiej to zrozumieć jeśli zna się domknięcia: http://pl.wikipedia.org/wiki/Domkni%C4%99cie_(programowanie).
Funkcja zwrócona przez prepare_comparison_operator jest przypisywana do this[func], czyli w tym wypadku będziemy mogli wywołać metodę lt obiektu klasy JAQ z parametrami field i value. Te paramery wykorzystuje funkcja podstawiona pod func i zwraca funkcję równoznaczną z:
[JAVASCRIPT] pobierz, plaintext
  1. function(obj){return obj['age'] < 27;}
[JAVASCRIPT] pobierz, plaintext

Tak się stało, ponieważ pod field podstawiliśmy 'age', a pod value 27. W 82 linijce sprawdzamy czy wywołaliśmy metodę not. Jeśli tak, trzeba negować wynik zwrócony przez funkcję, do której odnosi się zmienna cnd.
W 91 linijce łączymy razem wszystkie warunki, o ile były jakieś wcześniej podane. To jest pierwszy warunek jaki podaliśmy ( wiemy to bo w metodzie from przypisaliśmy do this.logical_operator wartość null ), więc teraz this.query równa się condition. Ale jeśli logical_operator odnosi się do jakiejś funkcji, do query przypisujemy wynik tej funkcji. Tak więc w czasie wywołania naszego drugiego operatora porównania (gt czyli "większe niż"), w 91 linijce zostanie wywołana funkja przypisana do logical_operator mająca postać (dla and):
[JAVASCRIPT] pobierz, plaintext
  1. function(expr1, expr2){
  2. return function(obj){
  3. return expr1(obj) && expr2(obj);};
  4. }
[JAVASCRIPT] pobierz, plaintext

Do expr1 podstawiamy query (czyli funkcję sprawdzającą czy obiekt spełnia kryteria, która powstała wcześniej podczas wywołania metod odpowiadających operatorom porównania). Do expr2 podstawiamy condition. Funkcja zwraca funkcję, która przyjmuje badany obiekt i zwraca odpowiednią wartość logiczną.

Co myślicie o takim podejściu do sprawy. Liczę na konstruktywną krytykę.