List - globules-io/OGX.JS GitHub Wiki

OGX.JS provides a SuperArray named List which extends the natural Javascript Array by adding methods without tempering any of the native ones. However, its power is found using array of objects, as it supports filtering, sorting and grouping by properties. It also converts properly to JSON using JSON.stringify.

If you want to use this single component only, scroll to the bottom of the page.

Instantiate

let array = new OGX.List(1,2,3);  //Standard array, doesn't offer much improvements over Array
let array = new OGX.List([1,2,3]); //You can also pass as array but it will give the same result (array ignored)
let array = new OGX.List({id:1},{id:2},{id:3});  //Array of objects, now we're talking!
let array = new OGX.List([{id:1},{id:2},{id:3}]);  //Same result as previous line, array ignored

Keep in mind this super array is designed primarily to handle lists of objects. If you wish to manipulate multi dimensional arrays such as [1,2], [3,4](/globules-io/OGX.JS/wiki/1,2],-[3,4) without worrying about data transformation, it is recommended to use a standard array.

For the rest of the documentation, please consider the following super array

let array = new OGX.List(
    {id:1, name:'Chad', age:20, gender:'male', registered:'2017-01-01', salary:10000},
    {id:2, name:'Sarah', age:30, gender:'female', registered:'2017-01-02', salary:25000},
    {id:3, name:'Tyrone', age:25, gender:'male',  registered:'2017-01-03', salary:35000},
    {id:4, name:'Stacy', age:40, gender:'female',  registered:'2017-01-04', salary:60000}
);

Extended methods

array.insert(_*_, _INDEX_);  //Insert item at position
array.delete(_INDEX_); //Delete item at position   
array.findDelete(_PROPERTY_, _VALUE_, _INT_LIMIT_); //Delete item(s) matching the property/value pair, returns the deleted item(s)
array.findReplace(_PROPERTY_, _VALUE_, _OBJECT_, _LIMIT_); //Find and replace all matching elements by a new element, returns an array of found elements before they were replaced
array.findUpdate(_PROPERTY_, _VALUE_, _OBJECT_, _BOOL_STRICT_, _LIMIT_); //Find and update partially or totally an element given another object. If _BOOL_STRICT_ is set true, only the existing properties are updated, none are created
array.findIndex(_PROPERTY_, _VALUE_); //Find index of the first element matching the property/value pair 
array.find(_PROPERTY_, _VALUE_, _INT_LIMIT_); //Returns a OGX.list with all matched items(s)
array.get(_QUERY_); //Returns a OGX.List with all matched items(s) based on a query object
array.update(_QUERY_, _OBJECT_, _BOOL_STRICT_, _LIMIT_) //Update items found with search query
array.unset(_QUERY_); //Find and delete items matching a query
array.deleteProperty(_PROPERTY_); //Delete a property for every document, returns number of altered documents
array.unique(_PROPERTY_, _MAP_); //Find unique values and remap (optional)
array.last(); //Returns the last item of the list
array.swap(_INDEX0_, _INDEX1_); //Swaps 2 items
array.clear(); //Remove all data and filters and indexes

All find methods return a single object if the limit is set to 1. Otherwise, they return a OGX.List with the matched document(s).

Note that find, findDelete and findIndex support regular expressions. The following line look for objects with the property name starting with the letter s and delete them and will stop after it has deleted 2 matches

array.findDelete('name', /^s/gi, 2);

To find the index of an object

array.findIndex('name', 'Sarah');  //Return an object of format {index:_INT_}

To find a maximum of 5 persons with name starting with a letter S

array.find('name', /^s/gi, 5); //Returns new OGX.List with match item(s)

To change the age to 50, for all males, do

array.findUpdate('gender', 'male', {age:50});

To change the salary and the age of 'Chad', do

array.findUpdate('name', 'Chad', {age:16, salary:1000}, false, 1);

Sorting

array.order(_PROPERTY_, _WAY_);

For instance, to sort by name descending

array.order('name', -1);

Filtering

OGX.List provides filtering operations inspired by mongodb. In order to filter the list, you must add filters to the super array then filter it. You can also remove filters.

array.addFilter(_PROPERTY_, _MODE_, _VALUE_); //Add a filter
array.removeFilter(_PROPERTY_); //Remove a filter
array.getFilters(); //Returns a filter object
array.setFilters(_FILTERS_); //Sets the filters
array.resetFilters(); //Remove all filters
array.filter(); //Filters the list and returns a new OGX.List
array.setFilteringMode(_STRING_); //Either "and" or "or", defaults to "and"

In the following example, we filter our (sorted now) list by only keeping female(s) older than 25 years old

array.addFilter('gender', 'eq', 'female');
array.addFilter('age', 'gt', 25);
let filtered_list = array.filter();

Note that the returned array is also a super array.

You can also import/exports filters between OGX.List instances, such as

let filters = array.getFilters();
some_other_array.setFilters(filters);

You can also take a shortcut to adding filters and use the get method, see querying

Filtering modes

Here is the list of available modes for filtering operations

 'eq' //Equal to
 'eqjson' //For deep objects, JSON comparison, equal to
 'neq' //Not equal to
 'in' //Contains
 'nin' //Doesn't contain
 'lt' //Lesser than
 'lte' //Lesser or equal to
 'gt' //Greater than
 'gte' //Greater or equal to
 'btw' //Between, expects value to be array [_from_, _to_]
 'substr' //Substring mode, equal to, expects value to be array [_from_, _to_, _niddle_]
 'regex' //Regex match
 'exist' //if the property exists

OGX.JS also supports filtering over dates and date-time objects (as string). For instance, to filter all people registered between the 2017-01-01 and 2017-01-04 (not inclusive), do

 array.addFilter('registered', 'btw', ['2017-01-01', '2017-01-04']);

Or to obtain people registered after the 1st of January, do

array.addFilter('registered', 'gt', '2017-01-01');

To obtain people whose first name starts by the letter S, you can do

array.addFilter('name', 'substr', [0, 1, 'S']);

You can do the equivalent using the regex operator

array.addFilter('name', 'regex', /^s/gi);

All of these filters produce the same result

array.addFilter('gender', 'in', 'fe');  
array.addFilter('gender', 'eq', 'female');
array.addFilter('gender', 'neq', 'male'); 
array.addFilter('gender', 'regex', /^fe/gi);
array.addFilter('gender', 'substr', [0, 2, 'fe']);

Note that filtering over strings is case insensitive. To filter case sensitive, use the regex operator. Also note that the 'in' and 'nin' operators also accept array to array comparison, and in this case the filtering is case sensitive and type sensitive. For the following example, consider that the elements of our list have tags such as ['red', 'blue', 'yellow', 'black']. To filter all elements that have BOTH red and blue tags, do

array.addFilter('tags', 'in', ['red', 'blue']);

Overall Filtering Mode

By default, OGX.List filtering will only return the items that match all filters. If you wish to get the items that match at least 1 filter, you can set the filtering mode to "or".

 array.setFilteringMode('or');

You can at anytime set it back to its default "and" mode

 array.setFilteringMode('and');

Custom filters

OGX.List also offers the possibility to extend its filtering capabilities by using custom filtering functions. All custom filters must return true or false.

 //custom filter receiving the value of the filtered property 
 function myCustomFilter(__val){
      if(...){
            return true;
      }
      return false;
 }

 //filtering with it
 array.addFilter('some_property', myCustomFilter, null);

Querying

You can also query the list by using the get method, which is basically a shortcut to creating filters one by one, by using a single object, such as

 let res = array.get({age:{gte:50, lte:78}, name:{in:'Chad'}});

being the exact same as

   array.addFilter('age', 'gte', 50);
   array.addFilter('age', 'lte', 78);
   array.addFilter('name', 'in', 'Chad');
   let res = array.filter();

And you can also add a sort object and a limit, such as

   let res = array.get({age:{gte:50, lte:78}, name:{in:'Chad'}}, {age:-1}, 5);

Which equates to

   array.addFilter('age', 'gte', 50);
   array.addFilter('age', 'lte', 78);
   array.addFilter('name', 'in', 'Chad');
   let res = array.filter(5);
   res.order('age', -1);

And you can also group your get result by passing a grouping such as

   let res = array.get({age:{gte:50, lte:78}, name:{in:'Chad'}}, {age:-1}, ['age'], 5);

And also multiple levels of grouping

   let res = array.get({age:{gte:50, lte:78}, name:{in:'Chad'}}, {age:-1}, ['age', 'salary], [null, 'numrange'], [null, [10000, '$', 'pre'](/globules-io/OGX.JS/wiki/'age',-'salary],-[null,-'numrange'],-[null,-[10000,-'$',-'pre')], 5);

For more information about grouping, keep scrolling.

You can also use the get function for deep path queries, such as

  let res = array.get({"travel.start":{eq:"2018-10-10"}});   

Note that since version 1.36.0, you can omit the eq and use a property/value pair instead

  let res = array.get({"travel.start":"2018-10-10"}); 

Updating

You can update one of more records matching a property/value pair

  array.findUpdate('name', 'Chad', {name:'Chael'});

You can also update one of more records using a search query.

  array.update({age:{gte:50, lte:78}, name:{in:'Chad'}}, {name:'Chael'});

Since version 1.3.19 of OGX.List (version 1.31.0 of OGX.JS), you can pass an update function, where you manually update the item, such as

  array.update({}, (__item) => { ... your own update function });

Querying and deleting

To delete from the list given a query, use the unset method, which returns a new OGX.List containing the deleted objects, or a single object if the limit is set to 1

 let obj = array.unset({name:{in:'Chad'}}, 1);
 let list = array.unset({name:{in:'Chad'}}, 5);  

Delete a common property

You can delete a property from every item of the list, such as

 array.deleteProperty('whatever');

Unique values

You can retrieve unique values for a common property by doing

  let values = array.unique('age', false);
  //[20, 25, 30 ...]

Which returns a standard array of values. But you can also return these unique values as an OGX.List

  let values = array.unique('age', true);
  //[{age:20}, {age:25}, {age:30} ...]

and you can also specify a new name for the property

   let values = array.unique('age', true, 'oldness');
  //[{oldness:20}, {oldness:25}, {oldness:30} ...]

Grouping

OGX.List can also return a grouped list by one property per group level

array.group(
    _BY_,   //Array or String, Common property to group by, 
   _MODE_,  //Array or String, The grouping mode, substr, numrange, date or a numeric value in days to group by
   _MODE_VALUE_  //Array or String, The value to use for the grouping mode
);

To group our array by age of groups of 10 years, do

array.group('age', 'numrange', 10);

numrange accepts up to 3 parameters, such as

 array.group('salary', 'numrange', [_VALUE_, _UNIT_, _POSITION_]);

Which is very useful to group by number and display a unit (dollars, meters, etc.). To group by salary with groups of $10,000 and display it as $low-high such as $10000-$20000, do

array.group('salary', 'numrange', [10000, '$', 'pre']);

POSITION only accepts 'pre' or 'post'

To group by month (year-month)

array.group('registered', 'date', 'month');

The supported mode_values for a grouping by date are 'year', 'month' and 'week'. You can also pass a numeric value instead representing the numbers of days. To group by groups of 80 days, do

array.group('registered', 'date', 80);

To group by the first letter of the name

array.group('name', 'substr', [0, 1]);

Simple grouping (same age)

array.group('age');

You can also group a list with virtually unlimited sub groups or properties. Take note that all items must have all the grouping properties. MODE in this case can be an array of modes to be used per group, but if defined as a string, it will be common to all groups.

For instance, you can do a simple multi level grouping like this

 array.group(['gender', 'salary']);

But in the case of the salary, we would want a different mode and mode_value to display the groups of salary properly, so we have to pass them both as array

 let data = array.group(['gender', 'salary'], [null, 'numrange'], [null, [10000, '$', 'pre']]);

Now the list is going to be grouped first by gender (simple grouping) and then it's going to group its sub items by salary ranges of $10000

The final result is a JSON tree as deep as many groups that you pass to it, with the elements of the array at the deepest level of the grouping

 //console.log(data);
 {
      "male":{
           "$0-10000":[items],
           "$10000-20000":[items],
            ...
      },
      "female":{
           "$0-10000":[items],
           "$10000-20000":[items],
            ...
      }
 }

Note that the array.group() method uses array.groupm() method if the first parameters is an array. As a safe net, always use array.group(). Also note that items are also held into an OGX.List.

Caching

If objects of your list all have a common index, and that you would like to speed up the process of fetching a single object using that index, you can cache your list. Once the cache has been turned on, any new object inserted to the list must have the index (property) to be added to the cache.

For instance, if all of your objects have the id property and you have a huge list, it could be slow to cycle the entire list to get a single object, moreover if that object is at the bottom of the list. And even worse so if, in your code, you constantly fetch objects one by one, by id.

 list.cache('id');

Then to retrieve an object by id without cycling the list, in this case an object of id equal to 100 limit 1

 let object = list.read('id', 100, 1);

You can also use a combined index to index documents by id and color.

 list.cache('size', 'color');

So later on, if you want to get all documents faster (if you only search by size and color), you can do

 let arr = list.read({size:'XL', color:'blue'}); 

And apply a limit. If the limit is set to one, a single object is return instead of a list.

 let arr = list.read({size:'XL', color:'blue'}, 5); 
 let obj = list.read({size:'XL', color:'blue'}, 1); 

To remove an index

 list.uncache('id');

To remove a combined index

 list.uncache('size', 'color');

Keep in mind that the initial cost in performance associated with caching a huge list is high but this happens only once. Then, the subsequent reads will be then much faster than using get, unless you remove items of your list; In this case, all the indexes matching the item will have to be rebuilt but they will be rebuild in the next thread. If you need to force the indexing, you can do (not recommended).

 list.rebuild();

Displaying

OGX.JS also provides the ability to render a List via many different components, with more often than not, a single line of code.

Render List as OGX.DynamicList (editable list)

Render List as OGX.GroupedList (editable grouped list as nested list)

Render List as OGX.TreedList (editable grouped list as OGX.Tree or OGX.StackedTree)

Considerations

If you are looking for a more complete solution to store and interact with a JSON database in the front end (and feed data to List), check out our mongodb inspired database for the front end MongOGX

Download

OGX.List is now available as a separate component HERE.