Javascript checking whether string is in either of two arrays - javascript

I'm pulling my hair out over this one. I have two arrays, likes & dislikes, both filled with about 50 strings each.
I also have a JSON object, data.results, which contains about 50 objects, each with an _id parameter.
I'm trying to check find all the objects within data.results that aren't in both likes and dislikes.
Here's my code at present:
var newResults = []
for(var i = 0; i<data.results.length; i++){
for(var x = 0; x<likes.length; x++){
if(!(data.results[i]._id == likes[x])){
for(var y = 0; y<dislikes.length; y++){
if(!(data.results[i]._id == dislikes[y])){
newResults.push(data.results[i]);
console.log("pushed " + data.results[i]._id);
}
else
{
console.log("They already HATE " + data.results[i]._id + " foo!"); //temp
}
}
}
else
{
console.log(data.results[i]._id + " is already liked!"); //temp
}
}
}
As you can see, I'm iterating through all the data.results objects. Then I check whether their _id is in likes. If it isn't, I check whether it's in dislikes. Then if it still isn't, I push it to newResults.
As you might expect by looking at it, this code currently pushes the result into my array once for each iteration, so i end up with a massive array of like 600 objects.
What's the good, simple way to achieve this?

for (var i = 0; i < data.results.length; i++) {
isInLiked = (likes.indexOf(data.results[i]) > -1);
isInHated = (dislikes.indexOf(data.results[i]) > -1);
if (!isInLiked && !isInHated) {
etc...
}
}

When checking whether an Array contains an element, Array.prototype.indexOf (which is ECMAScript 5, but shimmable for older browsers), comes in handy.
Even more when combined with the bitwise NOT operator ~ and a cast to a Boolean !
Lets take a look how this could work.
Array.prototype.indexOf returns -1 if an Element is not found.
Applying a ~ to -1 gives us 0, applying an ! to a 0 gives us true.
So !~[...].indexOf (var) gives us a Boolean represantation, of whether an Element is NOT in an Array. The other way round !!~[...].indexOf (var) would yield true if an Element was found.
Let's wrap this logic in a contains function, to simply reuse it.
function contains (array,element) {
return !!~array.indexOf (element);
}
Now we only need an logical AND && to combine the output, of your 2 arrays, passed to the contains function.
var likes = ["a","b","f"] //your likes
var dislikes = ["c","g","h"] //your dislikes
var result = ["a","c","d","e","f"]; //the result containing the strings
var newresult = []; //the new result you want the strings which are NOT in likes or dislikes, being pushed to
for (var i = 0,j;j=result[i++];) //iterate over the results array
if (!contains(likes,j) && !contains (dislikes,j)) //check if it is NOT in likes AND NOT in dislikes
newresult.push (j) //if so, push it to the newresult array.
console.log (newresult) // ["d","e"]
Here is a Fiddle
Edit notes:
1. Added an contains function, as #Scott suggested

Use likes.indexOf(data.results[i]._id) and dislikes.indexOf(data.results[i]._id).
if (likes.indexOf(data.results[i]._id) != -1)
{
// they like it :D
}

Try first creating an array of common strings between likes and dislikes
var commonStrAry=[];
for(var i = 0; i<likes.length; i++){
for(var j=0; j<dislikes.length; j++){
if(likes[i] === dislikes[j]){
commonStrAry.push(likes[i] );
}
}
}
then you can use this to check against data.results and just remove the elements that don't match.

Related

How to detect duplicate characters in an Array, and create an argument accordingly?

Good evening, I attempting to detect duplicate characters in a string. More specifically, I am trying to find up to two different duplicates within an Array. If there is one duplicate, add a sub-string, and if there is another duplicate, add a different sub-string. Is there any way to do this?
Here is some example code I have so far:
var CodeFieldArray = ["Z80.0", "Z80.1", "Z80.0", "Z70.4"];
/* We have an array here used to create the final string at the end of the
code. It is a dummy array with similar variables in my actual code. For
reference sake, there may be only one object in the array, or 7 total,
depending on the user's input, which is where the duplicate detection should
come in, in case the user enters in multiples of the same code. */
var i, Index;
for (i = 0, L = 0; i < CodeFieldArray.length; i++) {
Index = CodeFieldArray[i].indexOf(CodeFieldArray[i]);
if(Index > -1) L += 1;
Extra0 = CodeFieldArray.indexOf("Z80.8");
Extra1 = CodeFieldArray.indexOf("Z80.9");
if(L >= 2 && Extra0 == -1) CodeFieldArray.push("Z80.8");
Extra0 = CodeFieldArray.indexOf("Z80.8");
if(L >= 4 && Extra0 != -1 && Extra1 == -1) CodeFieldArray.push("Z80.9");
console.println(Extra0);
}
/*^ we attempted to create arguments where if there are duplicates
'detected', it will push, "Z80.8" or, "Z80.9" to the end of the Array. They
get added, but only when there are enough objects in the Array... it is not
actually detecting for duplicates within the Array itself^*/
function UniqueCode(value, index, self) {
return self.indexOf(value) === index;
}
CodeFieldArray = CodeFieldArray.filter(UniqueCode);
FamilyCodes.value = CodeFieldArray.join(", ");
/* this is where we turn the Array into a string, separated by commas. The expected output would be "Z80.0, Z80.1, Z70.4, Z80.8"*/
I have it to where it will add "Z80.8" or "z80.9" if they are not present, but they are being added, only if there are enough objects in the Array. My for-loop isn't detecting specifically the duplicates themselves. If there was a way to detect specifically the duplicates, and create an argument based off of that, then we would be doing grand. The expected output would be "Z80.0, Z80.1, Z70.4, Z80.8"
You can use Set and forEach and includes
var CodeFieldArray = ["Z80.0", "Z80.1", "Z80.0", "Z70.4"];
let unique = [...new Set(CodeFieldArray)];
let match = ['Z80.8','Z80.9'];
let numOfDup = CodeFieldArray.length - unique.length;
if(numOfDup){
match.forEach(e=>{
if(!unique.includes(e) && numOfDup){
unique.push(e);
numOfDup--;
}
})
}
console.log(unique.join(','))
So the idea is
Use Set to get unique values.
Now see the difference between length of original array and Set to get number of duplicates.
Now will loop through match array and each time we push item from match array into unique we reduce numOfDup by so ( to handle case where we have only one duplicate or no duplicate ).
In the end join by ,
You could do something like this:
var uniqueArray = function(arrArg) {
return arrArg.filter(function(elem, pos,arr) {
return arr.indexOf(elem) == pos;
});
};
uniqueArray ( CodeFieldArray )

How to run the loop inside the loop faster in javascript

I have two array objects which is of different lengths in which data.rows is of length 955 and place_names is of length 287. I am running loop inside the loop which takes around 3 minutes to run. Is there any easy and fastest way to run the code below?
for (i = 0; i < place_names.length; i++) {
for (j = 0; j < data.rows.length; j++) {
if (place_names[i].name === data.rows[j].name) {
geom.push(data.rows[j].st_asgeojson);
geom1.push({
name: data.rows[j].name,
geometry: data.rows[j].st_asgeojson
});
matched_average_value.push(place_names[i].average);
matched_sum_value.push(place_names[i].sum);
matched_minimum_value.push(place_names[i].minmum);
matched_maximum_value.push(place_names[i].maximum);
}
else {
console.log("no matches found");
}
}
}
You have two lists place_names and data.rows. The field you're interested in is the name field.
Depending on where you get this data from, it might be better to use a dictionary for this:
var dict = {};
place_names.forEach(
function(place_name) {
dict[place_name.name]=place_name;
}
);
Now you can loop through your data.rows:
data.rows.forEach(
function(row) {
if(row.name in dict) {
var place_name = dict[row.name];
// Do what needs to be done with "row" and "place_name"
}
}
);
This should reduce your n^2 algorithm to a n log n or even n (Depending on JavaScript's dictionary indexing complexity, I'm not sure). This solution assumes that all place_names[i].name values are unique. If they aren't, then you might want to store a list of all matching place_names in your dict.

How to remove an array element without splice and without undefined remaining?

Is it possible to remove an array element at a certain position, without rearranging indexes, and without that position changing to undefined?
I don't think that is possible with delete nor splice?
I need an accurate way to view the length of the array, without rearranging indexes.
I do not want to use splice because i have an object that has specific positions mapped to actual X,Y points of a tabel (Punkt).
UPDATE: actually, knowing if the array element exists out of ONLY undefined values might also help me, is there an easier way then looping through?
var keys = Object.keys(racks);
for (var i = 0; i < keys.length; i++)
{
for (var x = 0; x < racks[keys[i]].punkt.length; x++)
{
if(racks[keys[i]].punkt[x].y == fullName)
{
//delete racks[keys[i]].punkt[x];
racks[keys[i]].punkt.splice(x,1);
console.log(keys[i] + " : " + racks[keys[i]].punkt.length);
}
}
}
I don't think that is possible with delete nor splice?
I need an accurate way to view the length of the array, without rearranging indexes.
Then delete, a hasOwnProperty or in guard when retrieving from the array, and a loop counting the elements (or a separate variable keeping track) is the only way to do it. JavaScript's standard arrays are inherently sparse (because they're not really arrays at all), they can have gaps in them where they don't have entries. To create a gap, delete the array entry using delete.
Example:
// Setup
var a = ["a", "b", "c", "d"];
console.log(a.length); // 4
// Using delete
delete a[2]; // Delete the entry containing "c"
console.log(a.length); // Still 4
a.hasOwnProperty(2); // false
// Using the guard when getting an entry
if (a.hasOwnProperty(2)) { // Or `if (2 in a)`
// Get and use [2]
}
else {
// Do whatever it is you want to do when the array doesn't have the entry
}
// Finding out how many it *really* has:
var key;
var count = 0;
for (key in a) {
if (a.hasOwnProperty(key) && // See below
/^0$|^[1-9]\d*$/.test(key) &&
key <= 4294967294
) {
++count;
}
}
console.log(count); // 3
See this other answer for the details behind that if in the loop. If you never put non-element properties on the array, you can skip the second and third parts of that if.
This works perfectly.
var delrow = window.event.srcElement;
while ((delrow = delrow.parentElement) && delrow.tagName != "TR");
delrow.parentElement.removeChild(delrow);
var punten = racks[keys[i]].punkt.length;
if(racks[keys[i]].punkt[x].y == fullName)
{
delete racks[keys[i]].punkt[x];
punten--;
}
if(punten==0)
{
console.log("Could have removed device: " + keys[i]);
}

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?

Code doesn't work in foreach

Here is my code:
var divarray = document.getElementById("yui-main").getElementsByTagName("div");
var articleHTML;
var absHTML;
var keyHTML;
var bodyHTML = [];
for( var i in divarray) {
if(divarray[i].className == "articleBody"){
articleHTML = divarray[i];
for( var j in articleHTML ){
bodyHTML[i] = '';
if(articleHTML[j].className == "issueMiniFeature"){continue;}
if(articleHTML[j].className == "abstract"){absHTML = articleHTML[i]; continue;}
if(articleHTML[j].className == "journalKeywords"){keyHTML = articleHTML[i]; continue;}
bodyHTML[i] = articleHTML[i];
}
break;
}
i++;
}
The error I am getting is:
SyntaxError: Unexpected token var
I am using Google Chrome
The javascript for...in doesn't do what you would expect (which is enumerate through eleemnts in an array.
for...in in javascript will enumerate through the key/value pairs (or public variables) that make up the object (which isn't what you want).
You need to use a good, old fashioned for loop.
You can add this to your script:
Array.prototype.foreach = function (callback) {
for (var i=0; i < this.length; i++) {
callback(this[i]);
}
}
Then you simply do this:
myarray.foreach(function (currentItem) {
/*...do whatever with the currentItem...*/
});
I think you mistaking JavaScript for the functionality of PHP. JavaScript does not have foreach loops. JavaScript has for in, which is what you are incorrectly using and normal for loops. Use a standard for loop when dealing with arrays. You will need to use a for in loop with object literals because the index is not the simplicity of an incrementing positive integer.
In JavaScript a for loop has 3 parts in its argument separated by a semicolon as follows:
* start position of incrementor (optional if the variable is previous defined with 0 or a positive integer)
* end position of incrementor
* method of incrementation
In the following examples arrayName is value I made up for the name of an array:
for (; a < arrayName.length; a += 1) {
for (a = x + 1; a < arrayName.length + 3; a += 2) {
The for in loop argument has two required parts and a third part to prevent errors using an if condition:
* The value of an index to search for
* The name of the container in which to search
* The third part is an if condition
The following example will return the value supplied to the "book" index of the objectName object literal. objectName is a name I made for an example object literal:
for ("book" in objectName) {
if (objectName.hasProperty("book")) {
Why not use a traditional for loop instead? You're not really using an associative array here ...
That's not the right way to iterate over a collection.
You want a standard for loop, not a for..in loop
for( var i = 0, l = divarray.length; i < l; i++ ) {
There's something else, you then proceed to try to iterate over each element
for( var j in articleHTML ){
articleHTML at this point holds a reference to a single HTML node - not a collection or array of any sort.

Categories