Better solution to find a cell in an array - javascript

I have the following array:
var myArray = [
{
"id":1,
"name":"name1",
"resource_uri":"/api/v1/product/1"
},
{
"id":5,
"name":"name2",
"resource_uri":"/api/v1/product/5"
}
]
Each row is identified by it's unique id. I am quite new to Javascript and was wondering what was the best solution to find a cell based on id.
For example, for the id:5; my function must return:
findCell(myTable, id=5);
// this function must return:
{
"id":5,
"name":"name2",
"resource_uri":"/api/v1/product/5"
}
I'm quite afraid to do an ugly for loop... Maybe there is some built-in javascript function to perform such basic operations.
Thanks.

Yes there is a built-in function - filter. I would use it like this:
findCells(table, property, value) {
return table.filter(function (item) {
return item[property] === value;
});
}
findCells(myTable, "id", 5);
This is a bit modified version, of what you want: it can find all cells by the specified property name value.
Edit: using for loop to search the first occurence of the element is okay, actually:
findCell(table, id) {
var result = null;
for (var i = 0, cell = table[0], l = table.length; i < l; i++, cell = table[i]) {
if (cell.id === id) {
result = cell;
break;
}
}
return result;
}
findCell(myTable, 5);

Try this expression this might be helpful
var result = myArray.filter(function(element, index) { return element.ID == 5; });
filter() has two parameters
element - current row
index - current row index.

If you are going to stick with the array, the 'ugly' for loop is your best bet for compatibility. It's not that ugly when put in a function:
function findById(id) {
for(var i = 0; i < myArray.length; ++i) {
if(myArray[i].id === id) return myArray[i];
}
}
// Not checking return value here!
alert(findById(5).name);
Filter is another option if your concern is only with recent versions of browsers. It will return an array of values.
If your array is very large though, it would make sense to introduce some sort of index for efficient lookups. It adds an additional maintenance burden, but can increase performance for frequent lookups:
var index = {};
for(var i = 0; i < myArray.length; ++i) {
index[myArray[i].id] = i;
}
// Find element with id=5
alert(myArray[index[5]].name);
Example

Related

JavaScript Pop vs slice methods on an array

I've got a data set that's several hundred elements long. I need to loop through the arrays and objects and determine if the data in them is less than a certain number (in my case, 0). If it is, I need to remove all those data points which are less than zero from the data set.
I've tried .pop and .slice but I'm not implementing them correctly. I was trying to push the bad data into its own array, leaving me with only the good data left.
Here's my JS
for (var i = 0; i < data.length; i++) {
if (data[i].high < 0) {
console.log(data[i].high)
var badData = [];
badData.push(data.pop(data[i].high));
console.log(data[i].high)
}
}
I'd go with .filter():
const result = data.filter(row => row.high > 0);
In case you need the bad results too.
const { good, bad } = data.reduce((acc, row) => {
const identifier = row.high > 0 ? 'good' : 'bad';
acc[identifier].push(row);
return acc;
}, { bad: [], good: [] });

Javascript loop doesn't work in meteor

This should work but its not. What am I doing wrong? I want to output "selected" to tags I have on a meteor page
Template.editor.onRendered( function() {
var cats = ["Comedy","Action","Self Help"];
var arrayLength = cats.length;
for (var i = 0; i < arrayLength; i++) {
if(cats[i].indexOf(getDocument.category) != -1){
//found
var id = cats[i].trim().toLowerCase();
$("body").find("#"+id).attr("selected=selected");
console.log(id);
} else {
console.log(getDocument.category)
}
}
}
also
getDocument.category = ["Action", "Comedy"]
Maybe change
$("body").find("#"+id).attr("selected=selected");
with
$("body").find("#"+id).attr("selected","selected");
Edit:
if(cats[i].indexOf(getDocument.category) != -1){
I think you have here a wrong direction
try this instead:
if(getDocument.category.indexOf(cats[i]) != -1){
If I do not mistakenly understand what you asking for, you are trying to find the elements of 'cats' array if exist in the getDocument.category. If so, the above approach is wrong. Take a look at this line:
if(cats[i].indexOf(getDocument.category) != -1){...}
the result of this if checking will always returning -1, the explanation is below:
cats[i] will return the element (with index i) of cats, so if i=0 the result will be "Comedy". Then, indexOf will be executed on it, "Comedy".indexOf() ,
to find the position of getDocument.category (which is an array).
That's means you are looking for an array inside a string? that's will not work.
Actually, we can check if an element exists in array with includes methods. So maybe the complete code will be looked like this:
Template.editor.onRendered(function() {
var cats = ["Comedy", "Action", "Self Help"];
var arrayLength = cats.length;
for (var i = 0; i < arrayLength; i++) {
if (getDocument.category.includes(cats[0])) {
//found
var id = cats[i].trim().toLowerCase();
$("body").find("#" + id).attr("selected=selected");
console.log(id);
} else {
console.log(getDocument.category)
}
}
})
Hope this will help, thanks
You need to change a line to set selected attribute
$("body").find("#"+id).attr("selected","selected");
Or try the following:
$(document).find("#"+id).attr("selected","selected");

custom querySelectorAll implemention

This was given to me as an interview question -- didn't get the job, but I still want to figure it out.
The objective is to write two querySelectorAll functions: one called qsa1 which works for selectors consisting of a single tag name (e.g. div or span) and another called qsa2 which accepts arbitrarily nested tag selectors (such as p span or ol li code).
I got the first one easily enough, but the second one is a bit trickier.
I suspect that, in order to handle a variable number of selectors, the proper solution might be recursive, but I figured I'd try to get something working that is iterative first. Here's what I've got so far:
qsa2 = function(node, selector) {
var selectors = selector.split(" ");
var matches;
var children;
var child;
var parents = node.getElementsByTagName(selectors[0]);
if (parents.length > 0) {
for (var i = 0; i < parents.length; i++) {
children = parents[i].getElementsByTagName(selectors[1]);
if (children.length > 0) {
for (var i = 0; i < parents.length; i++) {
child = children[i];
matches.push(child); // somehow store our result here
}
}
}
}
return matches;
}
The first problem with my code, aside from the fact that it doesn't work, is that it only handles two selectors (but it should be able to clear the first, second, and fourth cases).
The second problem is that I'm having trouble returning the correct result. I know that, just as in qsa1, I should be returning the same result as I'd get by calling the getElementsByTagName() function which "returns a live NodeList of elements with the given tag name". Creating an array and pushing or appending the Nodes to it isn't cutting it.
How do I compose the proper return result?
(For context, the full body of code can be found here)
Here's how I'd do it
function qsa2(selector) {
var next = document;
selector.split(/\s+/g).forEach(function(sel) {
var arr = [];
(Array.isArray(next) ? next : [next]).forEach(function(el) {
arr = arr.concat( [].slice.call(el.getElementsByTagName(sel) ));
});
next = arr;
});
return next;
}
Assume we always start with the document as context, then split the selector on spaces, like you're already doing, and iterate over the tagnames.
On each iteration, just overwrite the outer next variable, and run the loop again.
I've used an array and concat to store the results in the loop.
This is somewhat similar to the code in the question, but it should be noted that you never create an array, in fact the matches variable is undefined, and can't be pushed to.
You have syntax errors here:
if (parents.length > 0) {
for (var i = 0; i < parents.length; i++) {
children = parents[i].getElementsByTagName(selectors[1]);
if (children.length > 0) {
for (var i = 0; i < parents.length; i++) { // <-----------------------
Instead of going over the length of the children, you go over the length of the parent.
As well as the fact that you are reusing iteration variable names! This means the i that's mapped to the length of the parent is overwritten in the child loop!
On a side note, a for loop won't iterate over the elements if it's empty anyway, so your checks are redundant.
It should be the following:
for (var i = 0; i < parents.length; i++) {
children = parents[i].getElementsByTagName(selectors[1]);
for (var k = 0; k < children.length; i++) {
Instead of using an iterative solution, I would suggest using a recursive solution like the following:
var matches = [];
function recursivelySelectChildren(selectors, nodes){
if (selectors.length != 0){
for (var i = 0; i < nodes.length; i++){
recursivelySelectChildren(nodes[i].getElementsByTagName(selectors[0]), selectors.slice(1))
}
} else {
matches.push(nodes);
}
}
function qsa(selector, node){
node = node || document;
recursivelySelectChildren(selector.split(" "), [node]);
return matches;
}

Add a value to each element in javascript

Is there a good way to add a certain value to each element in an array in javascript? Essentially, this should be a better way of writing the following:
a = [1,2,3,4];
for (i = 0; i < a.length; i++) {
a[i] += 7;
}
Maybe using map (but not necessarily)?
Edit:
Or a more interesting example:
a = [{'x':1},{'x':2},{'x':3},{'x':4}];
for (i = 0; i < a.length; i++) {
a[i].x += 7;
}
You can use map to do it:
a = a.map(function(entry) {
return entry + 7;
});
I'm not seeing how it's "better" to create a new array rather than update the one you have.
You can also use forEach:
a.forEach(function(entry, index) {
a[index] += 7;
});
It's still a bunch of function calls (but that's not a problem), but you have the advantage (over a for loop) of not having to declare the indexing variable, and you're modifying the existing array rather than replacing it.
Edit: Your "most interesting" example says even more that map is not really the best choice.
a.forEach(function(entry) {
entry.x += 7;
});
Yes, you can use .map but it will not modify the array in-place, so you must assign the result to a:
a = a.map(function(x) { return x+7 });

Can I select 2nd element of a 2 dimensional array by value of the first element in Javascript?

I have a JSON response like this:
var errorLog = "[[\"comp\",\"Please add company name!\"],
[\"zip\",\"Please add zip code!\"],
...
Which I'm deserializing like this:
var log = jQuery.parseJSON(errorLog);
Now I can access elements like this:
log[1][1] > "Please add company name"
Question:
If I have the first value comp, is there a way to directly get the 2nd value by doing:
log[comp][1]
without looping through the whole array.
Thanks for help!
No. Unless the 'value' of the first array (maybe I should say, the first dimension, or the first row), is also it's key. That is, unless it is something like this:
log = {
'comp': 'Please add a company name'
.
.
.
}
Now, log['comp'] or log.comp is legal.
There are two was to do this, but neither avoids a loop. The first is to loop through the array each time you access the items:
var val = '';
for (var i = 0; i < errorLog.length; i++) {
if (errorLog[i][0] === "comp") {
val = errorLog[i][1];
break;
}
}
The other would be to work your array into an object and access it with object notation.
var errors = {};
for (var i = 0; i < errorLog.length; i++) {
errors[errorLog[i][0]] = errorLog[i][1];
}
You could then access the relevant value with errors.comp.
If you're only looking once, the first option is probably better. If you may look more than once, it's probably best to use the second system since (a) you only need to do the loop once, which is more efficient, (b) you don't repeat yourself with the looping code, (c) it's immediately obvious what you're trying to do.
No matter what you are going to loop through the array somehow even it is obscured for you a bit by tools like jQuery.
You could create an object from the array as has been suggested like this:
var objLookup = function(arr, search) {
var o = {}, i, l, first, second;
for (i=0, l=arr.length; i<l; i++) {
first = arr[i][0]; // These variables are for convenience and readability.
second = arr[i][1]; // The function could be rewritten without them.
o[first] = second;
}
return o[search];
}
But the faster solution would be to just loop through the array and return the value as soon as it is found:
var indexLookup = function(arr, search){
var index = -1, i, l;
for (i = 0, l = arr.length; i<l; i++) {
if (arr[i][0] === search) return arr[i][1];
}
return undefined;
}
You could then just use these functions like this in your code so that you don't have to have the looping in the middle of all your code:
var log = [
["comp","Please add company name!"],
["zip","Please add zip code!"]
];
objLookup(log, "zip"); // Please add zip code!
indexLookup(log, "comp"); // Please add company name!
Here is a jsfiddle that shows these in use.
Have you looked at jQuery's grep or inArray method?
See this discussion
Are there any jquery features to query multi-dimensional arrays in a similar fashion to the DOM?

Categories