JQuery push array to specific element in array - javascript

I'm making a webapp with the help of JQuery to keep track of goals & habits.
One can add a main goal such as 'Discipline', and then afterwards can attach subgoals or habits to said main goal (e.g. 'work out everyday').
Organizing the array for main goals is obvious;
goals = ['Acceptance', 'Discipline', 'Accountability'];
I however have found no way in JQuery/Javascript to attach/add an array of items to a specific item in ANOTHER array.
Is there an easier way to do this, with JSON for example ?
Thanks in advance for any help offered

You do this by storing an array of objects at the top level, with a property for the child array
var goals = [{
name: "Acceptance",
children:[]
},{
name: "Discipline",
children:[]
},{
name: "Accountability",
children:[]
}];
When it comes to adding your child you just push it to the child array
goals[0].children.push("Work out every day");
Another option is store key/values at the top level
var goals = {"Acceptance":[],"Discipline":[],"Accountability":[]};
Slightly less versatile, but adding an item to a specific element slightly easier
goals["Acceptance"].push("Work out every day");

Related

Reading/writing nested data in a Firebase Database (web)

This seemingly simple question is surely a duplicate of many, so I apologize for that. Won't be offended when it's marked as such... But I have yet to find a clear, straight forward and modern answer that seems to work as I'd expect.
Given "Item One", how can I return the "subItems" array for the object that matches that title?
Additionally, how do I push new subitems to that array (if there's some different way I should target that array when writing rather than reading)
And the context of this is "items" is the top-level db.ref('items')
To find an item by a property, you need to run a query that orders and filters on that property. So in your case:
var ref = firebase.database().ref("items");
var query = ref.orderByChild("title").equalTo("Item One");
query.once("value", function(snapshot) {
snapshot.forEach(function(item) {
console.log(item.key); // -L8...EMj7P
console.log(item.child("subItems").val()); // logs the sub items
// console.log(item.ref.child("subItems").push().set(...)); // adds a sub item
});
});
Note that nesting data types is an anti-pattern in the Firebase Database, since it typically leads to problems later on. A more idiomatic approach is to have two top-level lists (e.g. items and subItems) that then use the same keys:
items: {
-L8...EMj7P: {
title: "Item One"
}
},
subItems: {
-L8...EMj7P: {
...
}
}

Adding objects to a Set in javascript

I am not sure if there is a set data structure in JS like in python. I have an array of objects like this in Javascript, the ID is unique. If I have two arrays like this - how can I combine them into a single array where I throw away duplicates based on the ID field?
First array:
[{
filed : "1-Jan-1970",
name: "John Smith",
ID: 1234
}
... (many more items)
]
Second array:
[{
filed : "1-Jan-1980",
name: "John Smith",
ID: 1234
}
... (many more items)
]
In the combined array I only want to keep one item with ID = 1234. I dont care which one is thrown away, how do I do this in Javascript that is also fast? I am looking at combining two lists with a couple of thousand items each and I want to keep only one record per ID from either array. Is there a compact way to combine the two arrays into one and then weed out the duplicates?

Efficient Sorted Data Structure in JavaScript

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.

Passing Location in Object as Variable

I have a large object, mixed with arrays of data (it's a treeview of folders and images - I have no control over what is outputted here.
For example:
var test = {
Folders: [{
Folders:[{
Folders:[{
Folders:[
{value:1},{value:2}
]
}]
}]
},{}
]
}
Value 1 (which in my case is an image) can be found here:
test.Folders[0].Folders[0].Folders[0].Folders[0].value
My end users are using a drop down to select their folder, I need to somehow pass the location via the drop down.
I've tried adding the "path" to the data-attr attribute of the drop down:
<option value="folder6" data-attr="[0].Folders[0].Folders[0].Folders[0]">Folder6</option>
However attempting to use that like this:
var myLocation = $('#element').find('option:selected').attr('data-attr');
//myLocation now is a string "Folders[0].Folders[0].Folders[0].Folders[0].value"
console.log(test[myLocation]
Doesn't work (it's down to the Arrays and numbers as passing a string as an object location in this fashion normally works).
I'm quietly confident I'm going about this the wrong way fullstop. I'm open to ideas on how better to do this in general, or how to get this horrible fudge to work.
There are many ways to solve this issue and it mostly depends on your needs and global architecture.
#1 The Evil Way (using eval)
var path = '[0].Folders[0].Folders[0].Folders[0]';
eval('test.Folders' + path); //Object {value: 1}
#2 Use an object as a map to index every folder
var foldersMap = {};
//loop over your tree and build the index
foldersMap[path] = folder;
//then retrieve it later
foldersMap[path];
#3 Store the object on the option directly
//while building the option
optionEl.folder = folder;
//then later retrieve it from the selected option
yourSelect.options[yourSelect.selectedIndex].folder;
#4 Create your own keypath function that can traverse an object structure based on a string keypath rather than using eval. I will provide an implementation as soon as I have more time.
There are probably many other ways, but these are just ideas.
Conceptually a tree can be n-levels deep. Baking in the path like that may not be a good ideas unless you know how "deep" the structure will be ahead of time.
It looks like you might be trying to show each potential folder in a drop down list (or maybe just the ones with images).
To do this, I would create a function to recursively loop through your json structure. The result of this function would either directly build the drop down menu or flatten the values into an array which could then be bound to the drop down menu. I would use a front-end templating library like handlebar.js, but you could also manipulate a dropdown control via JQuery.
Try storing it as a JSON array in data-attr. jQuery will pull it out as an array. Then you just need to find a way to loop it. Here's my first crack at it (with no validation and exception handling)...
HTML:
<select>
<option data-attr="[0,0,0,0]">Foo</option>
</select>
JavaScript:
var test = {
Folders: [{
Folders: [{
Folders: [{
Folders: [{
value: 1
}, {
value: 2
}]
}]
}]
}, {}]
};
var folderIndexes = $("option").data("attr"); // an array
alert(getValue(test, folderIndexes));
function getValue(folders, folderIndexes) {
for (var i = 0; i < folderIndexes.length; i++) {
folders = folders.Folders[folderIndexes[i]];
}
return folders.value;
}
Fiddle... http://jsfiddle.net/atn22/

Algorithm for data filter

Can you suggest me an algorithm for filtering out data.
I am using javascript and trying to write out a filter function which filters an array of data.I have an array of data and an array of filters, so in order to apply each filter on every data, I have written 2 for loops
foreach(data)
{
foreach(filter)
{
check data with filter
}
}
this is not the proper code, but in short that what my function does, the problem is this takes a huge amount of time, can someone suggest a better method.
I am using the Mootools library and the array of data is JSON array
Details of data and Filter
Data is JSON array of lets say user, so it will be
data = [{"name" : "first", "email" : "first#first", "age" : "20"}.
{"name" : "second", "email" : "second#second", "age" : "21"}
{"name" : "third", "email" : "third#third", "age" : "22"}]
Array of filters is basically self define class for different fields of data
alFilter[0] = filterName;
alFilter[1] = filterEmail;
alFilter[2] = filterAge;
So when I enter the first for loop, I get a single JSON opbject (first row) in the above case.
When I enter the second for loop (filters loop) I have a filter class which extracts the exact field on which the current filter would work and check the filter with the appropriate field of the data.
So in my example
foreach(data)
{
foreach(filter)
{
//loop one - filter name
// loop two - filter email
// loop three - filter age
}
}
when the second loop ends i set a flag denoting if the data has been filtered or not and depending on it the data is displayed.
You're going to have to give us some more detail about the exact structure of your data and filters to really be able to help you out. Are the filters being used to select a subset of data, or to modify the data? What are the filters doing?
That said, there are a few general suggestions:
Do less work. Is there some way you can limit the amount of data you're working on? Some pre-filter that can run quickly and cut it down before you do your main loop?
Break out of the inner loop as soon as possible. If one of the filters rejects a datum, then break out of the inner loop and move on to the next datum. If this is possible, then you should also try to make the most selective filters come first. (This is assuming that your filters are being used to reject items out of the list, rather than modify them)
Check for redundancy in the computation the filters perform. If each of them performs some complicated calculations that share some subroutines, then perhaps memoization or dynamic programming may be used to avoid redundant computation.
Really, it all boils down to the first point, do less work, at all three levels of your code. Can you do less work by limiting the items in the outer loop? Do less work by stopping after a particular filter and doing the most selective filters first? Do less work by not doing any redundant computation inside of each filter?
That's pretty much how you should do it. The trick is to optimize that "check data with filter"-part. You need to traverse all your data and check against all your filters - you'll not going to get any faster than that.
Avoid string comparisons, use data models as native as possible, try to reduce the data set on each pass with filter, etc.
Without further knowledge, it's hard to optimize this for you.
You should sort the application of your filters, so that two things are optimized: expensive checks should come last, and checks that eliminate a lot of data should come first. Then, you should make sure that checking is cut short as soon as an "out" result occurs.
If your filters are looking for specific values, a range, or start of a text then jOrder (http://github.com/danstocker/jorder) will fit your problem.
All you need to do is create a jOrder table like this:
var table = jOrder(data)
.index('name', ['name'], { grouped: true, ordered: true })
.index('email', ['email'])
.index('age', ['age'], { grouped: true, ordered: true, type: jOrder.number });
And then call table.where() to filter the table.
When you're looking for exact matches:
filtered = table.where([{name: 'first'}, {name: 'second'}]);
When you're looking for a certain range of one field:
filtered = table.where([{age: {lower: 20, upper: 21}}], {mode: jOrder.range});
Or, when you're looking for values starting with a given string:
filtered = table.where([{name: 'fir'}], {mode: jOrder.startof});
Filtering will be magnitudes faster this way than with nested loops.
Supposing that a filter removes the data if it doesn't match, I suggest, that you switch the two loops like so:
foreach(filter) {
foreach(data) {
check data with filter
}
}
By doing so, the second filter doesn't have to work all data, but only the data that passed the first filter, and so on. Of course the tips above (like doing expensive checks last) are still true and should additionally be considered.

Categories