I can't figure out how to work with iterations in foreach of KOjs.
What I need is to group contents of an array like this
Var x = [{name: Joel, sex: male}, {name: Eric, sex: male},{name:Elliot, sex:female}, {name:Mary, sex:female}]
So the resulting data-bind foreach would display the sex of the person, but only once as a label.
Something like this
-male
Joel
Eric
-female
Elliot
Mary
With normal for loops it is as simple as making a variable that holds last entry's gender, and prints the new one if it changes. Something like
If(this.sex != cachedSex){
cachedSex=this.sex;
console.log(cachedSex);
}
But I can't figure it out how to make this in KO.js
Please give me an advice.
Knockout lets you use computed observables which solve this problem in a clever way :)
If you want a full code example here is a fiddle illustrating this solution.
Lets say all your people are called people and it's an observableArray :
var that = this;
this.people = ko.observableArray([{name: Joel, sex: male}, {name: Eric, sex: male},{name:Elliot, sex:female}, {name:Mary, sex:female}])
Now we only want the males and females apart:
this.males = ko.computed(function(){
return that.people().filter(function(person){
return person.sex === "male";
});
});
this.females = ko.computed(function(){
return that.people().filter(function(person){
return person.sex === "females ";
});
});
(Of course - if you repeat this sort of code a lot - make a function out of it instead of repeating yourself :) )
Then you can do the foreach binding normally, once for females and once for males
Benjamins answer is correct but there is another way without having to create additional observableArrays. In the view you can simply do a foreach binding and put a sliver of logic there to handle filtering. Its the simplest way but down the road you could argue there may be alternatives such as benjamins which are more suited, depending on your application
Men
This would allow you to do some basic filtering in the view. You could also set up a custom binding handler that you could pass in the filter to make it even more efficient and clean
Related
I am a complete beginner and I am working on creating a multiple choice quiz using array. The questions have the same format " Which word in the following has a similar meaning to '_________' ". So I create an array(?) that store all the words I want to put in '________' and another array that contain answers correspond to it.
var ques=['apple', 'pencil' ,'juice'];
var ans= ['orange', 'pen','water'];
However, as I want to create a few hundreds of questions, I think it is quite troublesome to do it in the following way.
var querep=[ ['apple','orange'], ['pencil','pen'],['juice','water'] ];
so i try doing this instead:
var querep=[ [ques, ans] ];
and yes, i know it makes no sense to u all, but i just want to merge the two array lists and allow it to do the same function as
var querep
example questions:
which word in the following has similar meaning to apple?
A.pen
B.writer
C.orange
D.vegetable
This answer may not be fulfilled with your approach. But you can have it as reference for data structure design. Since years ago I did something similar to this and finally found myself immerse in refactor phase and re-do a lot of parts.
I can tell you may have a big list of question survey, when it comes to long list. You should think of it as a big object. Not a usual Array like the approach you're implementing, because it's faster for any things related to find/ traverse purpose.
So, basically the data might look like this:
const questions = {
"apple": "orange",
"water": "juice",
"chicken": "duck",
...
}
You still can iterate through an object with {key,value} pair and your problem is solved.
In real scenario, I think the data structure might be a more complicated, usually each will have its own _id, so the object might look like this, but the approach of iterating doesn't change.
const questions = {
"12312311414": {
id: "12312311414",
quesion: "apple",
accept: "orange",
},
"12312311415": {
id: "12312311415",
quesion: "water",
accept: "juice",
},
...
}
So, before deciding merging two arrays, I hope you can change your mind.
If you want to use a merged array as mentioned in question, you could use map function on array to create a new array. Here new querep array should be in the format as you want:
var ques=['apple', 'pencil' ,'juice'];
var ans= ['orange', 'pen','water'];
var querep = ques.map(function(val, index){
return [val, ans[index]]
});
plan like this.
// At first write all your questions , options and answers here.
var questions = [
{"question" : "what is the capital of india?" , "options" : ["hyderabad","delhi","chennai","bengalore"], "answer" : "hyderabad"},
{"question" : "what is even number?" , "options" : [1,3,5,8], "answer" :8}
];
// you will get question along with question number here
var questions_list = {}; var i = 1;
$.each(questions ,function(k,v){
questions_list[i] = v;
i++;
});
console.log(questions_list);
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'm working on something to give users a preview of changes they have made in a form where I compare serialised arrays of objects from the forms fields before and after changes. This all works fine, but I needed to exclude the 'product[territory_ids][]' elements for which there are many as I don't need such a deep level of comparison. The code below works fine.
// Get product form values before any changes are made
// and serialise them into an array of objects
$(".product_form_edit").ready(function() {
form_before = $(".product_form_edit").serializeArray()
// Using Underscore JS take out all off the product[territory_ids][] elements
// as they cause comparison to fail.
// We'll do a count comparision of the number of territories set separately
$.each(form_before, function(i){
form_before = _.without(form_before, _.findWhere(form_before, {name: 'product[territory_ids][]'}));
});
console.log(form_before);
});
What I do need to do and am struggling with is detecting a change in the number of 'product[territory_ids][]' elements that are checked.
I thought some kind of variation of:
$.each(form_before, function(i){
form_before = _.without(form_before, _.findWhere(form_before, {name: 'product[territory_ids][]'}));
});
Like:
$.each(form_before, function(i){
_.countBy(form_before, _.findWhere(form_before, {name: 'product[territory_ids][]'}));
}).length;
Might work, but this and lots of other attempts just return undefined.
Can anyone help? I'm sure it's way simpler than i'm making it.
Okay, so, after a little messing around with an example I came up with, I came to the conclusion that you probably want to be using _.filter to get your array of checkboxes. filter will always return an array, so you should get a count of 0 when there are no checkboxes checked.
$('#serialize').click(function() {
var data = $('#testForm').serializeArray(),
checkboxes = _.filter(data, function(i) {
return i.name === 'xyz';
});
console.log(data, checkboxes);
console.log(data.length, checkboxes.length);
});
I am building a web application in which I build a sorted list out of an object like this:
{head: {subhead: [list_items], subhead: [list_items]}, head: {subhead: [list_items]}}.
My problem is that I have to ensure the headings and subheading always follow in a certain order. This is complicated by the fact that headings and subheadings that may be added later on also need to follow this order. So the only way I could think to inform the parser of the order would be to give it some data like this:
{heads: [head1, head2, head3], subheads: {head1: [subhead1_1, subhead1_2], head2: [subhead2_1, subhead2_2, subhead2_3]}},
but that strikes me as overly verbose and repeating data that would be in the original data structure.
You might as well use an array (or your own structure) for this since you want it to be ordered. Your own structure might look like:
function Head(name) {
this.name = name;
this.arr = [];
}
So instead of the structure:
var thing = {
food: {fruit: [apples, oranges], vegetables: [carrots, peas]},
animals: {domestic: [cat, dog], wild: [lion, bear]}
}
You might do:
var thing = [new Head('food'), new Head('animals')]
Then:
thing[0].arr.push('apples');
thing[0].arr.push('oranges');
etc.
Of course you don't have to make a class, since you can actually attach properties to arrays in javascript. But making the datastructure would be a bit more of a pain.
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.