How to implement bi-directional connection between two arrays? - javascript

In my app I have an ItemsService which gets Items from Server and stores them as JSON objects in its cache variable. Items can be present in many places, e.g. in table or in graph/chart and so on.
For example, when I initialize a table - I need to pick only specific Items from cache, e.g. 1st, 3rd, 7th.
How to implement bi-directional connection between them? Basically I want table to contain references to specific items from cache so when I change an Item either in cache or in table - its state will be in sync all the time because it's the same Item.
Also when I delete Item from table - it needs to be removed from cache.
Here's example of table and cache structures:
Table:
table: {
"36": { // it's a name of a row
"72": [items], // it's a name of a column with corresponding items
"73": [items],
"74": [items]
},
"37": {
"72": [],
"73": [items],
"74": [items]
},
"38": {
"72": [],
"73": [],
"74": []
}
}
ItemsService cache (simplified version):
ItemsService = {
cache: [items]
};
Item structure:
{
id: 3,
parent_id: 1,
name: 'First Item',
siblings: [1,2,3],
active_users: [{user_id: 1, avatar_url: 'http://...'}, ...],
// 50 more fields :)
}
Also need to point out that I use angular-ui-sortable plugin to allow dragging of Items between columns/rows and I need to provide ng-model with array(I think). Here's how it looks right now:
<td ui-sortable="vm.sortableOptions"
ng-model="vm.table[row.id][column.id]">
<sb-item itemid={{item.id}}
ng-repeat="item in vm.table[row.id][column.id]">
</sb-item>
</td>

Unless for some reason you have to use two separate arrays, have you considered using a filter?

Your best bet would be objects. Variables that hold objects in javascript aren't really holding the object but a reference to said object. When you pass that variable into another one, the reference value gets copied so both variables point toward the same object.
var a = { 0: 'Property 0' };
var b = a;
b[0] = 'Property 0!'; //a[0] also the same.
delete b[0]; //a[0] nor b[0] exist anymore.

Using objects (rather than JSON) would work.
Then your cache and your table both point to same item objects. If you change something in the object, it is reflected at both ends.
var cacheArray = [{ item: 1 }, { item: 2}];
table[0][0] = cacheArray[0];
console.log(table[0][0].item); // 1
cacheArray[0].item = 9;
console.log(table[0][0].item); // 9.
Please note that the array and the table have not changed. They still point to the same objects.

Related

IndexedDB: Can you use an array element as a key or an index?

Consider the following object store, with the domain key set as the keyPath
var tags = [
//codes: 0 - markdown wrap tag
// 1 - HTML wrap tag
// 2 - single tag
{ domain: "youtube",
bold:["*",0],
strikethrough:["-",0],
italic:["_",0]
},
{ domain: "stackoverflow",
bold:["<strong>",1],
italic:["<em>",1],
strikethrough:["<del>",1],
superscript:["<sup>",1],
subscript:["<sub>",1],
heading1:["<h1>",1],
heading2:["<h2>",1],
heading3:["<h3>",1],
blockquote:["<blockquote>",1],
code:["<code>",1],
newline:["<br>",2],
horizontal:["<hr>",2]
}
];
The above code works fine and lets me do look-ups easily and efficiently. However, there are many cases where two objects in the store are completely identical except for their domain attribute.
For example, I want to add objects for all of the Stack Exchange sites to the store, and all of those objects would be equal to the one for StackOverflow.
So, rather than create many separate objects, I want to do something like this:
var tags = [
//codes: 0 - markdown wrap tag
// 1 - HTML wrap tag
// 2 - single tag
{ domain: ["youtube"],
bold:["*",0],
strikethrough:["-",0],
italic:["_",0]
},
{ domain: ["stackoverflow","stackexchange",...],
bold:["<strong>",1],
italic:["<em>",1],
strikethrough:["<del>",1],
superscript:["<sup>",1],
subscript:["<sub>",1],
heading1:["<h1>",1],
heading2:["<h2>",1],
heading3:["<h3>",1],
blockquote:["<blockquote>",1],
code:["<code>",1],
newline:["<br>",2],
horizontal:["<hr>",2]
}
];
Would it be possible to use a KeyGen rather than a keyPath and set up some kind of index that took a value and searched for it in the arrays pointed to by the domain key?
Or would I have to use a cursor each time I want to do a look up?
Some potentially helpful references are:
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB
http://www.w3.org/TR/IndexedDB/#key-path-construct
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
The solution is to use an index with the multiEntry key property set to true
see this link (thanks #kyaw Tun)
Each index also has a multiEntry flag. This flag affects how the index behaves when the result of evaluating the index's key path yields an Array. If the multiEntry flag is false, then a single record whose key is an Array is added to the index. If the multiEntry flag is true, then the one record is added to the index for each item in the Array. The key for each record is the value of respective item in the Array.
Armed with this index, a specific keyPath is no longer necessary, so you can just use a keyGen for simplicity.
So, to create the database:
request.onupgradeneeded = function(event)
{
var db = event.target.result;
var objectStore = db.createObjectStore("domains", {autoIncrement: true });
objectStore.createIndex("domain", "domain", { unique: true, multiEntry: true });
for(var i in tags)
{
objectStore.add(tags[i]);
console.log("added " + tags[i]["domain"] + " to the DB");
}
};
and an example of using a domain to query for an object:
var objectStore = db.transaction("domains").objectStore("domains");
var query = objectStore.index("domain").get(queryURL);
query.onsuccess = function(event){...};

Enumerate JSON master detail arrays

I have a view model that contains 2 related (master / detail) arrays that I want to bind to.
var ViewModel = function (data) {
var self = this;
self.CategoryList = ko.observableArray(data.CategoryList);
self.ItemList = ko.observableArray(data.ItemList);
}
Where Category is an array of { categoryID: 1, name: "Red Items" } etc
and Item is an array of { itemID: 1, categoryID: 1, name: "Red Widget" } etc
I have a handful of categories and possibly several hundred items. I am struggling with how to represent these in my view model in a way that I can enumerate categories and get to associated items or across all items and get to categories.
It doesn't seem efficient to maintain duplicate information on both lists and I don't want to be sending more data than needed over the wire.
One approach: create Item and Category objects and modify the prototype to include filtering (ko.computed) functions. category.Items() or item.Category().Name but I am not 100% how to best accomplish this within knockout.js given my need to load and save data.
I am not wed to arrays of simple JSON objects. Assuming I go the function / prototype route, how do I load and save my arrays of objects?
I think that you require a better data structure than an array, since -as you said- it's not optimal to keep duplicate information. Instead of having an array listing your categories, why don't use a hash table, or as we simply know in javascript, an object?
{
1: "Red Items",
2: "Blue Items",
3: "Green Items",
4: "Yellow Items",
}
(You can use a simple method to create the hash from the array)
var createHashFromArray = function(arr) {
var hash = {}
for (var i = 0, var len = arr.length; i < len; i++)
hash[i] = arr[i];
return hash;
}
From there, you can bind your viewModel according to the categories you need. You can even set up them as dynamic arrays using ko.computed in order to organize them in your view.
<div data-bind="foreach: redItemsOnly">
<span data-bind="text: itemId">ID</span>
<span data-bind="text: name">NAME</span>
</div>
And in your javascript...
self.redItemsOnly = ko.computed(function() {
return ko.utils.arrayFilter(self.ItemList(), function(item)
{
return self.CategoryHash[item.categoryID] === "Red Items"
});
}, self)
I'm not sure if this is what you want, but I hope this approach helps you to find other ways to come up with what you need.

Recursively (or iteratively) make a nested html table with d3.js?

I have an array of nested JSON structures where they have varying depth and not the same set of keys everywhere:
[
{
"name":"bob",
"salary":10000,
"friends":[
{
"name": "sarah",
"salary":10000
},
{
"name": "bill",
"salary":5000
}
]
},
{
"name":"marge",
"salary":10000,
"friends":[
{
"name": "rhonda",
"salary":10000
},
{
"name": "mike",
"salary":5000,
"hobbies":[
{
"name":"surfing",
"frequency":10
},
{
"name":"surfing",
"frequency":15
}
]
}
]
},
{
"name":"joe",
"salary":10000,
"friends":[
{
"name": "harry",
"salary":10000
},
{
"name": "sally",
"salary":5000
}
]
}
]
I wanted to use D3 to render this as nested html tables. For example the friends column will have tables showing the name, and salary of the friends of the individual referenced in the row. Sometimes one of these tables will have another level of a sub table.
I imagine the way to do this is by recursively creating tables. I wrote a python program that takes a JSON structure like this, and renders tables within tables, and the easiest way to do that was recursively. I see on the d3.js documentation there is a .each() thing you can call, which I am sure is what I need, I just need a little boost getting there (https://github.com/mbostock/d3/wiki/Selections#wiki-each).
So is there a nice way to do this in D3? I found this great example for rendering a 2d matrix of data as a table Creating a table linked to a csv file. With that tutorial I was able to get the outer most level of this data-structure rendered as a table, but I am stuck on how to go into levels recursively as needed, as of now they just show up as "Object" in the table since I am not treating them differently from normal strings and numbers.
Also I found this other question/answer that is similar to my question, but I really don't understand javascript well enough to see where/how the recursion is happening and readapt the solution to fit my needs: How do I process data that is nested multiple levels in D3?. Any advice or pointers to tutorials on recursively or iteratively processing nested tree like JSON data-structures in D3 would be much appreciated!
A recursive function would probably be good approach. See code below for one possible implementation (assuming your data is stored in jdata). See the comments in the code for some explanation and see this Gist for a live version: http://bl.ocks.org/4085017
d3.select("body").selectAll("table")
.data([jdata])
.enter().append("table")
.call(recurse);
function recurse(sel) {
// sel is a d3.selection of one or more empty tables
sel.each(function(d) {
// d is an array of objects
var colnames,
tds,
table = d3.select(this);
// obtain column names by gathering unique key names in all 1st level objects
// following method emulates a set by using the keys of a d3.map()
colnames = d // array of objects
.reduce(function(p,c) { return p.concat(d3.keys(c)); }, []) // array with all keynames
.reduce(function(p,c) { return (p.set(c,0), p); }, d3.map()) // map with unique keynames as keys
.keys(); // array with unique keynames (arb. order)
// colnames array is in arbitrary order
// sort colnames here if required
// create header row using standard 1D data join and enter()
table.append("thead").append("tr").selectAll("th")
.data(colnames)
.enter().append("th")
.text(function(d) { return d; });
// create the table cells by using nested 2D data join and enter()
// see also http://bost.ocks.org/mike/nest/
tds = table.append("tbody").selectAll("tr")
.data(d) // each row gets one object
.enter().append("tr").selectAll("td")
.data(function(d) { // each cell gets one value
return colnames.map(function(k) { // for each colname (i.e. key) find the corresponding value
return d[k] || ""; // use empty string if key doesn't exist for that object
});
})
.enter().append("td");
// cell contents depends on the data bound to the cell
// fill with text if data is not an Array
tds.filter(function(d) { return !(d instanceof Array); })
.text(function(d) { return d; });
// fill with a new table if data is an Array
tds.filter(function(d) { return (d instanceof Array); })
.append("table")
.call(recurse);
});
}

How can i navigate through the json?

I have some JSON which I have in a object but I can seem to return the values a sample of the json is as follows.
{
"rootLayout":"main",
"layoutDescriptions":[
{
"id":"main",
"container" : {
"type":"Tabs",
"content":[
{
"type":"Panel",
"label":"Simple Address",
"layout":"SimpleForm",
"comment":"This form is simple name value pairs",
"content":[
{ "type":"label", "constraint":"newline", "text":"Org Name" },
{ "type":"text", "property":"propOne" },
{ "type":"label", "constraint":"newline", "text":"Address" },
{ "type":"text", "property":"addrLine1" },
{ "type":"text", "property":"addrLine2" },
{ "type":"text", "property":"addrLine3" },
{ "type":"label", "constraint":"newline", "text":"Postcode" },
{ "type":"text", "property":"postcode" }
]
},
I am trying to return the rootLayout using
obj[0].rootLayout.id
This doesn't work also I am wondering how to access the content elements.
I am new to json and I have been thrown in the deep end I think. I cannot find any good reading on the internet can anyone recommend some.
Thanks.
Some explanation because you don't seem to understand JSON
It's not as complicated as one may think. It actually represents javascript objects as if they'd be written by code.
So if you have JSON written as:
{
id : 100,
name: "Yeah baby"
}
This means that your object has two properties: id and name. The first one is numeric and the second one is string.
In your example you can see that your object has two properties: rootLayout and layoutDescriptions. The first one jsonObj.rootLayout is string and will return "main" and the second one is an array:
layoutDescriptions: [ {...}, {...},... ]
Apparently an array of objects because array elements are enclosed in curly braces. This particular array element object that you provided in your example has its own properties just like I've explained for the top level object: id (string), container (another object because it's again enclosed in curlies) etc...
I hope you understand JSON notation a bit more.
So let's go to your question then
You can get to id by accessing it via:
jsonObj.layoutDescriptions[0].id
and further getting to your content objects:
var contentObjects = jsonObj.layoutDescriptions[0].container.content[0].content;
for(var i = 0; i < contentObjects.length, i++)
{
// assign this inner object to a variable for simpler property access
var contObj = contentObjects[i];
// do with this object whatever you need to and access properties as
// contObj.type
// contObj.property
// contObj.text
// contObj.constraint
}
Mind that this will only enumerate first content object's content objects... If this makes sense... Well look at your JSON object and you'll see that you have nested content array of objects.
The object is an object, not an array, and it doesn't have a property called 0.
To get rootLayout:
obj.rootLayout
However, rootLayout is a string, not an object. It doesn't have an id. The first item in the layoutDescriptions array does.
obj.layoutDescriptions[0].id
Are you trying to get one of layoutDescriptions with id equals to obj.rootLayout?
var targetLayout = {};
for(var i = 0; i < obj.layoutDescriptions.length; i++) {
if(obj.layoutDescriptions[i].id == obj.rootLayout) {
targetLayout = obj.layoutDescriptions[i]; break;
}
}
console.log(targetLayout);

How to get the object which represents the selected row in a slickgrid

I have a webpage with a slickgrid which renders an array of objects on a grid. I want to do some dynamic stuff (a master-detail like setup) when the selected row changes. However to be able to do this, I would need the data of the selected row. I know that the grid.getCurrentCellNode() function will give me the dom element of the current node, but what I am looking for is a javascript object. For instance, if I use an array of objects like the one below
data = [
{id:1, name:'Khaja', dob:'26/07/1985'},
{id:2, name:'Iqbal', dob:'26/07/1935'}
......
...
];
and if my selected row is the row with id equal to 2, I want to be able to retrive the object {id:2, name:'Iqbal', dob:'26/07/1935'} Is there a way through which I can get this object?
You can use the onSelectedRowsChanged event and the getSelectedRows method.
data[i]={
ID: json[i].ID,
Name: json[i].Name,
Description: json[i].Description,
CreatedDate: myDate,
makesub: "---",
shared: json[i].IsShared
};
.....
grid.onClick = function (e, row, cell) {
if (columns[cell].id == "colname"){
// where colname is the column on which you want to trigger the click
alert("clicked:"+row+", albumID:"+data[row].ID);

Categories

Resources