I have an array of arrays where the position of the value is important in that this data is used to ultimately layout a grid.
[[a,b,c,d],[a,b,c,d][a,b,c,d],[a,b,c,d]] // Original data
Based on an action, values within this "dataset" can change, but the size will never change.
[[a,b,c,d],[a,b,b,b][a,c,c,c],[a,b,c,d]] // Modified data
What I'd like to do is to return a dataset that only contains the delta values:
[[null,null,null,null,],[null,null,b,b],[null,c,null,c],[null,null,null,null,]]
Now, I do always know the max X and Y of the dataset, and could simply loop through the original dataset, comparing it's value to the corresponding value and build a new array, but it seems like there could be a more efficient way to accomplish this.
That said, my js-fu is minimal at best, and that's why I'm brining the problem here. Are there any language provided methods for accomplishing this? Suggested approaches? etc?
A bit of array mapping should do the trick.
Here's a fiddle: http://jsfiddle.net/C8jnr/
Given the following:
var arr_A = [[a,b,c,d],[a,b,c,d],[a,b,c,d],[a,b,c,d]];
var arr_B = [[a,b,c,d],[a,b,b,b],[a,c,c,c],[a,b,c,d]];
function deltaArrays(arr1, arr2)
{
return arr1.map(function(el_arr,i,arr){
return el_arr.map(function(el, j){
return (el == arr2[i][j]) ? null : arr2[i][j];
});
});
}
Calling deltaArrays on the two will yield the expected delta array;
deltaArrays(arr_A, arr_B) = [[null,null,null,null,],[null,null,b,b],[null,c,null,c],[null,null,null,null,]]
I don't believe there is a built in way to do this. Your best bet is to loop through each of the array elements and compare them with the new array, adding it to a list/dictionary if it is different.
Related
I have a javascript array of nested data that holds data which will be displayed to the user.
The user would like to be able to apply 0 to n filter conditions to the data they are looking at.
In order to meet this goal, I need to first find elements that match the 0 to n filter conditions, then perform some data manipulation on those entries. An obvious way of solving this is to have several filter statements back to back (with a conditional check inside them to see if the filter needs to be applied) and then a map function at the end like this:
var firstFilterList = _.filter(myData, firstFilterFunction);
var secondFilterList = _.filter(firstFilterList, secondFilterFunction);
var thirdFilterList = _.filter(secondFilterList, thirdFilterFunction);
var finalList = _.map(thirdFilterList, postFilterFunction);
In this case however, the javascript array would be traversed 4 times. A way to get around this would be to have a single filter that checks all 3 (or 0 to n) conditions before determining if there is a match, and then, inside the filter at the end of the function, doing the data manipulation, however this seems a bit hacky and makes the "filter" responsible for more than one thing, which is not ideal. The upside would be that the javascript Array is traversed only once.
Is there a "best practices" way of doing what I am trying to accomplish?
EDIT: I am also interested in hearing if it is considered bad practice to perform data manipulation (adding fields to javascript objects etc...) within a filter function.
You could collect all filter functions in an array and check every filter with the actual data set and filter by the result. Then take your mapping function to get the wanted result.
var data = [ /* ... */ ],
filterFn1 = () => Math.round(Math.random()),
filterFn2 = (age) => age > 35,
filterFn3 = year => year === 1955,
fns = [filterFn1, filterFn2, filterFn2],
whatever = ... // final function for mapping
result = data
.filter(x => fns.every(f => f(x)))
.map(whatever);
One thing you can do is to combine all those filter functions into one single function, with reduce, then call filter with the combined function.
var combined = [firstFilterFunction, seconfFilterFunction, ...]
.reduce((x, y) => (z => x(z) && y(z)));
var filtered = myData.filter(combined);
I have an array that is initialized like such var generationObject = [{string:"", score: 0}];
which I then fill dynamically:
for(var i = 0; i < amount_offspring; i++)
{
// "load" text into array and send the string to see if it evolves
generationObject[i].string = evolve(start_text, characters, mutation_rate);
// then score the string
generationObject[i].score = score(target_text, generationObject.string);
}
I then want to sort this array by score. I don't know what's best, to sort it in the for loop or sort the entire array afterwards.
I will then take the string of the highest scoring object and pass it through the function again, recursively.
So what would be a good way to go about this sort function? I've seen some here use this
generationObject.sort(function(a, b) {
return (a.score) - (b.score);
});
But I'm not sure if .sort is still supported? This didnt seem to work for me though.
generationObject is an array, not an object, so score(target_text, generationObject.string); could be the problem, as .string will be undefined. (Did you mean generationObject[i].string?)
Try building your array like this:
var generationObject = []
for(var i = 0; i < amount_offspring; i++)
{
evolved_string = evolve(start_text, characters, mutation_rate)
generationObject.push({
string: evolved_string,
score: score(target_text, evolved_string)
})
}
And then Array.prototype.sort should do the trick.
You should write your sorting logic outside the for loop, since if you put it inside, the object array will be sorted N times, where N being the iterations of your loop. The following are two ways to do it-
By using sort() function- To clarify your question, sort() is still supported across almost all the browsers. If you are still concerned about the browser compatibility, you can check the MDN documentation to see the list of supported browsers.
generationObject = generationObject.sort(function(a, b) {
return parseInt(a.score) - parseInt(b.score);
});
By using underscorejs-
In underscore, you can take advantage of the sortBy() function.
Returns a (stably) sorted copy of list, ranked in ascending order by the results of running each value through iteratee. iteratee may also be the string name of the property to sort by (eg. length).
You can simply do this in underscorejs-
generationObject = _.sortBy(generationObj, 'score');
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 have an array of elements in Javascript
var axes = [{id: "a"}, {id: "b"}]
and I have one of these items in a variable
var axis = {id: "b"}
I wanted a one liner in JavaScript (similar to the Linq one liners I can do in C#) where I will get the index of the array in which this element exists. So, in this example I will get the answer 1.
How is this possible?
You can use array prototype map method:
var axisIndex = axes.map(function(x) {return x.id; }).indexOf(axis.id);
https://stackoverflow.com/a/16100446/1414562
And to support older browsers, you could use the jQuery's way:
var axisIndex = $.inArray(axis.id, $.map(axes, function(x){return x.id}));
It looks like you want to create a new axis object that looks the same as one of the elements in the axes array, but isn't actually an element of the axes array. If this is the case, the indexOf method won't work because it would expect to find the exact object you're searching for in the array.
There are ways of doing this - it would usually involve more than one line though (the one line restriction seems fairly trivial). However, I've found a solution, despite it looking rather ugly, and better suited to a code golfing problem than real live code.
axes.indexOf(axes.filter(function(el) {if (el.id === axis.id) return true; }).shift());
i am trying to remove some items in an json object list, the ones that have a specific group. My JSON looks like this.
var events = [
{"id":"19","name":"sports","group":"1"},
{"id":"20","name":"school","group":"2"},
{"id":"21","name":"fun","group":"1"}
]
I tried this
for(var i in events)
if(events[i].group == deleted_group)
events.splice(i, 1);
But the problem of this, is that if i have to remove more items, it bugs out. Is there another easy way to to this ? I am open for sugestion even using underscore.js .
Thank you in advance, Daniel!
Try this
var events = [
{"id":"19","name":"sports","group":"1"},
{"id":"20","name":"school","group":"2"},
{"id":"21","name":"fun","group":"1"}
]
console.log(_.reject(events, function(event){ return event.group == '1'; }));
When you're using the "splice" function to remove elements from the array inside a for loop,
you need to shift your current index back when removing an item since the array is reindexed.
Also take a look at the array functions like "filter" for a more convenient way, read more on MDN.
You can use delete operator to delete objects (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete):
delete events[0]
The problem with delete is, that in your array, as a value of events[0] it will leave undefined.
So another way (the way I would choose for your simple example) is to just rewrite the array:
var temp_events = [];
for(var i in events)
if(events[i].group != deleted_group)
temp_events[temp_events.length] = events[i];
events = temp_events;
Executing splice in a for loop has complexity n^2 (where n is number of elements). Rewriting has linear complexity.