I am trying to optimize accessing cost of nested objects. I have the following structure (example):
Now I want to access data but the problem is I need to keep on adding loops where every I got nested data. That means if I want to access racks I need to itterate 3 for loops like
var jsonObj=[{
"shelfs": [
{
"Shelf1": [
{
"Racks": [
{
"Rack1": [
{
"Book1": "Value"
}
]
},
{
"Rack2": [
{
"Book1": "Value"
}
]
}
]
}
]
},
{
"Shelf2": [
{
"Racks": [
{
"Rack1": [
{
"Book1": "Value"
}
]
},
{
"Rack2": [
{
"Book1": "Value"
}
]
}
]
}
]
}
]
}];
for(var i=0;i<jsonObj.length;i++)
{
var shelfs=jsonObj[i];
var key=Object.keys(shelfs)[0];
//var shelfs=arr[arr[0].key];
//alert(JSON.stringify(shelfs[key]));//shelfs));
for(var j=0;j<shelfs[key].length;j++)
{
var shelfdetails=shelfs[key][j];
var skeys=Object.keys(shelfdetails);
for(var k=0;k<skeys.length;k++)
{
var racks=shelfdetails[skeys[k]];
alert(JSON.stringify(racks));
}
}
}
Here to access racks information I put 3 nested for loops but eventually it is increasing the time complexity. Please can anybody suggest me better data structure or method to access nested JavaScript objects with low time complexity?
You have n books that you want to display in your UI. It will take n display operations to display n books. It does not matter that they are in nested loops, the total number of display operations is still n. There is no optimization you can perform to reduce the number of display operations you need to perform.
Even if you were to flatten your data structure in to a single flat array of books the number of display operations would still be n.
I am trying to optimize accessing cost of nested objects.
Do you mean the CPU cost, the storage cost, or the code complexity cost? The three have quite different implications. Since you go on to say
I need to keep on adding loops whereever I got nested data.
I am going to assume that you are most interested in code complexity. In that case, consider the following flatter data structure, which might be easier to loop through, to filter, to sort, to group, and to otherwise process using utility libraries such as underscore.
[
{ shelf: 'Shelf1', rack: 'Rack1', book: 'Book1', value: "Value"},
{ shelf: 'Shelf1', rack: 'Rack2', book: 'Book1', value: "Value"},
{ shelf: 'Shelf2', rack: 'Rack1', book: 'Book1', value: "Value"},
{ shelf: 'Shelf2', rack: 'Rack2', book: 'Book1', value: "Value"}
]
Abstractly speaking, each "Book1": "Value" item has associated with it a shelf and a rack. In your suggested data structure, this "association" is represented by "belonging to" relationships, where it belongs to an array which is the value of a property whose name specifies the shelf or rack. In the above flatter structure, the associations are instead specified explicitly by giving them as properties.
With the flatter structure, if for some reason you wanted to create a data object with keys giving the shelf and values giving an array of objects on that shelf, in Underscore that is as easy as
_.groupBy(obj, 'shelf')
So all else being equal it seems that the flatter data structure is a more flexible way to represent the data, and you can derive other things you need from it more easily.
Another way to look at it is that currently in order to find the sets of relationships of shelves, racks, and books, you need to iterate through three levels of nested arrays, whereas in the flatter structure the relationships are represented more directly.
Performance, either CPU-wise or space-wise, is rarely going to be a reason to choose one structure over another, unless you are dealing with a huge amount of data. Otherwise, the difference in performance is likely to measured in milliseconds or microseconds, or a few K of storage. You should choose the structure that allows you to represent your algorithms in a fashion which is concise and provably correct. If you intend to handle hundreds of thousands of objects, then in that case yes, you would want to design custom structures optimized for time or space.
Related
There are two array of objects one from database and one from csv. I required to compare both array object by their relative properties of Phones and emails and find duplicate array among them. Due to odd database object structure I required to compare both array with Javascript. I wanted to know what is the best algorithm and best way of compare and find duplicates?
I explain simple calculations.
There are 5000 contacts in my database and user may upload another 3000 contacts from csv. Everytime we requires to find duplicate contacts from database and if they find then it may overwrite and rest should be insert. If I compare contact row by row then it may loop 5000 database contacts x 3000 csv contacts = 15000000 time traverse.
This is my present scenario I face due to this system goes stuck. I require some efficient solution of this issue.
I develop the stuff in NodeJS, RethinkDB.
Database object structure exactly represent like that way and it may duplicate entry of emails and phones in other contacts also.
[{
id: 2349287349082734,
name: "ABC",
phones: [
{
id: 2234234,
flag: true,
value: 982389679823
},
{
id: 65234234,
flag: false,
value: 2979023423
}
],
emails: [
{
id: 22346234,
flag: true,
value: "test#domain.com"
},
{
id: 609834234,
flag: false,
value: "test2#domain.com"
}
]
}]
Please review fiddle code, if you want: https://jsfiddle.net/dipakchavda2912/eua1truj/
I have already did indexing. The problem is looking very easy and known in first sight but when we talk about concurrency it is really very critical and CPU intensive.
If understand the question you can use the lodash method differenceWith
let csvContacts = [] //fill it with your values;
let databaseContacts = .... //from your database
let diffArray = [] //the non duplicated object;
const l = require("lodash");
diffArray = l.differenceWith(csvContact,
databaseContacts,
(firstValue,secValue)=>firstValue.email == secValue.email
Is it possible to apply the changes provided by an observer to another object (similar object as the one observed) ?
E.g:
properties: {
boardBucket: {
type: Object,
value: {
items: [],
currentBoardId: 1
}
},
remoteBoardBucket: { // let's assume this is on another client
type: Object,
value: {
items: [],
currentBoardId: 1
}
}
},
observers: [
"_boardBucketChanged(boardBucket.*)"
],
_boardBucketChanged: function(changeRecord) {
// `applyChanges()` doesn't exist but can it be done?
this.remoteBoardBucket.applyChanges(changeRecord);
}
Why I'm asking this:
I'm building a collaborative editor where both parties manipulate their own boardBucket, adding/removing items(boards) to it and adding/removing items in those boards (boardBucket.items[2].boardItems is an array for each board that holds items in that board).
The clients communicate via web-sockets between them and I'd like to see if it's possible to send only the changeRecord through the sockets and have it applied on the other-client's boardBucket.
This will speed up things significantly as it negates the need to send the whole boardBucket back and forth (I'm not really doing this but for the sake of the question I'm putting it out here)
An important note here is that I'm observing nested arrays as well. While board.items is an array which holds the boards, each board also holds another array called boardItems which holds the items in those boards. Therefore there is an arbitrary number of levels of depth of changes I'm observing here.
I am trying to take a JSON list that is formatted as such: (real list has over 2500 entries).
[
['fb.com', 'http://facebook.com/']
['ggle.com', 'http://google.com/']
]
The JSON list represents: ['request url', 'destination url']. It is for a redirect audit tool built on node.js.
The goal is to put those JSON value pairs in a javascript object with a key value array pair as such:
var importedUrls = {
requestUrl : [
'fb.com',
'ggle.com'
],
destinationUrl : [
'https://www.facebook.com/',
'http://www.google.com/'
]
}
Due to the sheer amount of redirects, I do prefer a nonblocking solution if possible.
You first need to create your object:
var importedUrls = {
requestUrl: [],
destinationUrl: []
}
Now, let's say you have your data in an array called importedData for lack of a better name. You can then iterate that array and push each value to its proper new array:
importedData.forEach(function(urls){
importedUrls.requestUrl.push(urls[0]);
importedUrls.destinationUrl.push(urls[1]);
});
This will format your object as you want it to be formatted, I hope.
I will propose it to you that you take another approach.
Why not have an array of importedUrls, each one with its correspondent keys?
You could have something like:
importedUrls = [
{
requestUrl: 'req',
destinationUrl: 'dest'
},
{
requestUrl: 'req2',
destinationUrl: 'dest2'
},
]
I'm sure you can figure out how to tweak the code I showed to fit this format if you want to. What you gain with this is a very clear separation of your urls and it makes the iterations a lot more intuitive.
I'm looking for a way to take a bunch of JSON objects and store them in a data structure that allows both fast lookup and also fast manipulation which might change the position in the structure for a particular object.
An example object:
{
name: 'Bill',
dob: '2014-05-17T15:31:00Z'
}
Given a sort by name ascending and dob descending, how would you go about storing the objects so that if I have a new object to insert, I know very quickly where in the data structure to place it so that the object's position is sorted against the other objects?
In terms of lookup, I need to be able to say, "Give me the object at index 12" and it pulls it quickly.
I can modify the objects to include data that would be helpful such as storing current index position etc in a property e.g. {_indexData: {someNumber: 23, someNeighbour: Object}} although I would prefer not to.
I have looked at b-trees and think this is likely to be the answer but was unsure how to implement using multiple sort arguments (name: ascending, dob: descending) unless I implemented two trees?
Does anyone have a good way to solve this?
First thing you need to do is store all the objects in an array. That'll be your best bet in terms of lookup considering you want "Give me the object at index 12", you can easily access that object like data[11]
Now coming towards storing and sorting them, consider you have the following array of those objects:
var data = [{
name: 'Bill',
dob: '2014-05-17T15:31:00Z'
},
{
name: 'John',
dob: '2013-06-17T15:31:00Z'
},
{
name: 'Alex',
dob: '2010-06-17T15:31:00Z'
}];
The following simple function (taken from here) will help you in sorting them based on their properties:
function sortResults(prop, asc) {
data = data.sort(function(a, b) {
if (asc) return (a[prop] > b[prop]);
else return (b[prop] > a[prop]);
});
}
First parameter is the property name on which you want to sort e.g. 'name' and second one is a boolean of ascending sort, if false, it will sort descendingly.
Next step, you need to call this function and give the desired values:
sortResults('name', true);
and Wola! Your array is now sorted ascendingly w.r.t names. Now you can access the objects like data[11], just like you wished to access them and they are sorted as well.
You can play around with the example HERE. If i missed anything or couldn't understand your problem properly, feel free to explain and i'll tweak my solution.
EDIT: Going through your question again, i think i missed that dynamically adding objects bit. With my solution, you'll have to call the sortResults function everytime you add an object which might get expensive.
I have the following example client side object:
var obj = {
"locations": [
[
37.502917,
-122.501335
],
[
37.494473,
-122.499619
],
[
37.484394,
-122.455673
]
],
"types": [
[
"type1"
],
[
"type2"
],
[
"type3"
]
]
};
Locations could contain up to 50 values. An ajax request returns a set of new locations and I need to evaluate if they are already within obj.locations. Each new returned location is a string e.g:
var test = 37.502917 + ',' + -122.501335;
For each location I can iterate through the current ones and check if it is present:
for(var i = 0; i < obj.locations.length; i++) {
if(obj.locations[i] == test){
console.log('Found!');
}
}
Is there a more efficient way of doing this as iterating through the object for each new location seems inefficient?
EDIT: My Solution:
I decided to take the locations object and turn in to a string, then evaluate each of the incoming strings:
var test = -121.60183 + ',' + 38.025783;
var cords = [].concat([], obj.locations).toString();
if( cords.indexOf(test) !== -1) {
console.log('found! ');
}
This is perhaps one of the oldest problems in computer science--looking something up.
You first have to ask yourself if it's worth worrying about. Perhaps it will take 1ms to find the location with a linear search, but 0.5ms with some kind of optimized search. So, is it worth the trouble?
The next approach would be to sort the list of locations, and do a binary search into it.
Another approach is to create some kind of hash table. You could use JavaScript objects for this, with properties as hash keys. The simplest approach would be to use lat+long as the property key, but now you've just shifted the efficiency problem to the efficiency of JS looking up keys in large objects.
You could design your own custom hash-like approach, where all locations with the same integral portion of latitude are stored as an array under a hash of 37. Then the performance is governed by the time taken to find the hash key in the table, and then looking through the smaller number of locations within its array.
Proceeding further, if performance is truly an issue, you could build some kind of tree structure for optimal lookup. At some point, you have to start trading off between the cost of building and updating the tree, and the savings from looking things up using the tree.
It is for sure inefficient, but unless you have to deal with thousands of those objects it will not hang your browser.
However, you can index the locations in an associative array and then use that to check for presence or absence of an element.
For example, you could add a locations_index object to your object, like this :
var obj = {
"locations": [
[
37.502917,
-122.501335
],
[
37.494473,
-122.499619
],
[
37.484394,
-122.455673
]
],
"locations_index" : {
"37.502917,-122.501335" : true,
"37.494473,-122.499619" : true,
// ...
},
"types": [
[
Then you can check if it is or not in the location_index with :
if (obj.locations_index["37.502917,-122.501335"]) {
// It's already there
} else {
Obviously, you need to take care of adding the new locations (and removing the ones you remove) from both the "real" array and the "index".