strings_to_check = ["a", "b", "c"]
test_arrrays = [ [ "a", "c", "e", "g"], [ "v", "x", "y", "z"] ]
What is the right way to check if each array in test_arrays contains any of the strings in strings_to_check array - ie. a, b or c
I could do the following, but it has its downside that even if one of the strings is present, it still check's for the rest.
for(let i = 0; i < test_arrrays.length; i++){
for(let j = 0; j < strings_to_check.length; j++){
if(test_arrrays[i].indexOf(strings_to_check[j]) > -1) {
console.log("matched");
}
}
}
This can be much more simply done with higher order functions, instead of devolving to using for loops and a slew of indices.
We want to see if all test_arrrays elements meet some criteria, so we know we should use every:
test_arrrays.every(/* some criteria */);
Now we just have to find out what that criteria is. "contains any of the strings in strings_to_check" Sounds like we need to use some on test_array, to find out if any of its strings are contained in strings_to_check. So our "criteria" will be:
test_arrray => test_arrray.some(s => strings_to_check_set.includes(s))
putting it together, we get:
test_arrrays.every( test_arrray =>
test_arrray.some(s => strings_to_check_set.includes(s))
)
includes has linear time complexity, so we can improve this algorithm by using a Set, and replacing includes with has, which has constant time complexity., to obtain this final result:
strings_to_check = ["a", "b", "c"]
test_arrrays = [ [ "a", "c", "e", "g"], [ "v", "x", "y", "z"] ]
strings_to_check_set = new Set(strings_to_check)
test_arrrays.every(test_arrray =>
test_arrray.some(s => strings_to_check_set.has(s))
)
Assuming that you want to check if every array from test_arrays contains at least one element from the strings_to_check array, you could use mix of Array#every and Array#some functions.
var strings_to_check = ["a", "b", "c"],
test_arrrays = [ [ "a", "c", "e", "g"], [ "v", "x", "y", "z"] ],
res = test_arrrays.every(v => v.some(c => strings_to_check.indexOf(c) > -1));
console.log(res);
If you have multiple test_arrrays, it makes sense to convert the strings_to_check into a Set for constant time lookup. The overall time complexity then reduces from O(m n) to O(m + n log n) where n is the number of elements in strings_to_check and m is the total number of elements in all test_arrrays and O(n log n) is the Set setup time.
A generic check function would then look as follows:
// Check if each haystack contains at least one needle:
function check(needles, haystacks) {
needles = new Set(needles);
return haystacks.every(haystack => haystack.some(element => needles.has(element)));
}
// Example:
console.log(check(
["a", "b", "c"],
[["a", "c", "e", "g"], ["v", "x", "y", "z"]]
));
If you only need to match one string just add break just like this :
for(/*loop settings*/){
/*some code*/
for(/*loop settings*/){
/*some code*/
if(/*some conditional statement*/){
/*handling stuff*/
break;
}
}
}
Related
I recovered an array with values from a previous method and I need to go through it and assign each of the values a key in alphabetical order.
["T", "C", "α", "T(linked)", "C"]
This is my array, previously mentioned. I want to know how, when crossing the array will I be able to assign it a key of alphabetical form, so that the final result was:
["A:T", "B:C", "C:α", "D:T(linked), "E:C"]
Any advice will be welcome. Thank you in advance.
You can achieve it by using Array.prototype.map function.
let a = ["T", "C", "α", "T(linked)", "C"];
a = a.map((val,index)=>{ return `${String.fromCharCode(65 + index )}:${val}`; } ) ;
console.log(a);
By having single elements, you could just map the values with a prefix.
var data = ["T", "C", "α", "T(linked)", "C"],
result = data.map((v, i) => `${(i + 10).toString(36).toUpperCase()}:${v}`);
console.log(result);
In your input array:
["T, C", "α", "T(linked), C"]
are "T, C" and "T(linked), C" supposed to be elements as is, or are you missing quotation marks? In other words, did you mean this?
["T", "C", "α", "T(linked)", "C"]
You can use the map function. Using the code
newArray = array.map((value, index) => ...)
will generate a new array with a function done for each value and index of the old array.
array = ["T", "C", "α", "T(linked)", "C"];
newArray = array.map((value, index) => `${String.fromCharCode(index + 65)}:${value}`);
newArray will be set to
["A:T", "B:C", "C:α", "D:T(linked)", "E:C"]
If you don't understand that method of concatenating strings, use this instead.
array = ["T", "C", "α", "T(linked)", "C"];
newArray = array.map((value, index) => String.fromCharCode(index + 65) + ":" + value);
I am trying to create a function that is called by multiple different buttons, and that selects a specific array depending on which button called the function.
For example:
If a button with id="Anterior" called the function, the function selects array named plusAnterior:
var plusAnterior = ["p", "b", "t", "d", "ɸ", "β", "f", "v", "θ", "ð", "s", "z", "m", "n", "l", "ɬ", "r", "ɾ"];
var minusAnterior = ["c", "ɟ", "k", "g", "ʃ", "ʒ", "x", "ɣ", "ɲ", "ŋ", "j", "w", "ʡ", "h"];
var toAdd = [];
function addFeature(featureId) {
if (activeFeatures === 0) {
toAdd = // plusAnterior called here
}
}
The problem is, I cannot simply do toAdd = plusAnterior because I want my function to work for any button pressed. I thought of doing something like getElementById("plus" + featureId), but I can't since the arrays I'm trying to select are not in my HTML.
Thank you for any help.
In javascript, you can use an object almost as a key/value array.
var testObject = {
featureId_a: [1, 2, 3, 4, 5], // etc
featureId_b: [1, 2, 3, 4, 5] // etc
}
Then we can return it with your method like so...
function addFeature(featureId) {
return testObject[featureId];
}
I think this is what you mean. Otherwise, you could use a 2D array and select based on the index. But it depends on your data really the best option.
For brevity and some sanitization, you should, of course, check the object for the property.
if(testObject.hasOwnProperty(featureId)){
return testObject[featureId];
}
I'm trying to create a music game where I have to generate a 3D array from a basic 2D array. The plan was to copy and paste the 2D array 4 times into the 3D array before modifying it, as shown:
var note3base = [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];
var note3 = new Array(4);
for (h=0;h<note3.length;h++){
note3[h] = note3base;
} //creates 4 copies of note3base in a 3d-array to be modified
for (i=0;i<note3[0].length;i++){
note3[1][i][1] = flat(note3[1][i][1]); //minor
note3[2][i][1] = flat(note3[2][i][1]);
note3[2][i][2] = flat(note3[2][i][2]); //dim
note3[3][i][2] = sharp(note3[3][i][2]); //aug
} //how did everything become the same?
The problem now seems to be that the for loop seems to apply the method to every single array (0 through 3).
The desired output for note3[0][1] would be C E G, note3[1][1] would be C Eb G, note[2][1] would be C Eb Gb, note[3][1] would be C E G#.
Any help is greatly appreciated!
I've included the (working) sharp and flat methods below for reference:
function sharp(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "x");
} else if (note.charAt(1) == "b"){
newnote = note.replace("b", "");
} else {
newnote = note + "#";
}
return newnote;
}
function flat(note){
var newnote;
if (note.charAt(1) == "#"){
newnote = note.replace("#", "");
} else {
newnote = note + "b";
}
return newnote;
}
The problem is that when you assign a variable equal to an array like this:
someVar = someArray;
...it doesn't make a copy of the array, it creates a second reference to the same array. (This applies to all objects, and arrays are a type of object.) So after your loop, where you've said:
for (h=0;h<note3.length;h++){
note3[h] = note3base;
...all of the elements in note3 refer to the same underlying array.
To make an actual copy, you can manually copy all of the elements across using a loop, or you can use the .slice() method to make a copy for you:
for (h=0;h<note3.length;h++){
note3[h] = note3base.slice();
}
But that will only solve half of the problem, because note3base itself contains references to other arrays, and .slice() will just copy these references. That is, although note3[0] and note3[1] (and 2 and 3) will refer to different arrays, note3[0][0] and note3[1][0] and note3[2][0] and note3[3][0] will refer to the same ["C", "E", "G"] array. (And so forth.)
You need what's called a "deep copy". You could do it with a nested loop:
for (h=0;h<note3.length;h++){
// create this element as a new empty array:
note3[h] = [];
// for each 3-note array in note3base
for (var k = 0; k < note3base.length; k++) {
// make a copy with slice
note3[h][k] = note3base[k].slice();
}
}
Having said all that, I think an easier way to do the whole thing would be instead of having a note3base variable that refers to an array, make it a function that returns a new array:
function makeNote3Array() {
return [
["C", "E", "G"],
["C#", "E#", "G#"],
["Db", "F", "Ab"],
["D", "F#", "A"],
["Eb", "G", "Bb"],
["E", "G#", "B"],
["F", "A", "C"],
["F#", "A#", "C#"],
["Gb", "Bb", "Db"],
["G", "B", "D"],
["Ab", "C", "Eb"],
["A", "C#", "E"],
["Bb", "D", "F"],
["B", "D#", "F#"],
["Cb", "Eb", "Gb"]
];
}
Because the function uses an array literal it will create a brand new array of arrays every time it is called. So then you can do the following, with no need for .slice() or nested loops:
var note3 = new Array(4);
for (h=0;h<note3.length;h++){
note3[h] = makeNote3Array();
}
TL;DR, do this:
for (h=0;h<note3.length;h++){
note3[h] = note3base.slice(0);
}
Explanation:
The problem is coming from the difference between passing something'by value' and 'by reference' in Javascript.
When you assign a primitive value to a variable, like a = "string";, and then assign that to another variable, like b = a;, the value is passed to b 'by-value': its value is assigned to b, but b references a different part of memory. There are now two "string" values in the memory, one for a, and one for b.
a = "string";
b = a;
a = "gnirts";
console.log(b); // "string"
This is not how it works for non-primitive types, such as arrays. Here the value is passed to b 'by reference', meaning that there is still only one [1, 2, 3] array in the memory, and both a and b are pointing at it. This means that is you change an element in a, it will change for b as well, because they reference the same array in memory. So you get this:
a = [1, 2, 3];
b = a;
a[0] = "hello";
console.log(b); // ["hello", 2, 3]
b[0] has changed because it references the same location in memory as a[0]. To get around this problem, we need to explicitly make a copy of note3base when passing it to another variable, rather than just passing it by reference. We can do this with note3base.slice(0) as above.
Edit: read more here
I have two arrays:
arr1 = ["a", "b", "c", "d", "e", "f", "g"]
arr2 = ["z", "q", "a", "v", "y", "e"]
I want to produce a an array of results to be this:
results = ["aa", "b", "c", "d", "ee", "f", "g"]
Basically I want to push into a results array everything in order in arr1 and if it has a match combine with the matching key, if not still list the item from arr1. So the results array is every item in arr1 but if it matched anywhere in obj 2 take the matching key and combine it. Note: results[0] is arr1[0] + arr2[2] not arr1[0] + arr1[0]
My actual problem is more complex, but I think this is the best simplification I can deduce. I am looking for a nested loop (or any way that works) that would produce this. Any help would be greatly appreciated, I know I'm close but so far I have not been able to produce this, and have been pulling my hair out trying. Thank you!
Answer by Juhana:
var results = arr1.map(function (val) {
return val + (arr2.indexOf(val) > -1 ? val : '');
});
I have the following problem with .push() method:
var myArray = ["a", "b", "c", "d"];
function add(arr) {
arr.push("e");
return arr;
}
add(myArray);
// myArray is now ["a", "b", "c", "d", "e"]
Why it overrides myArray? Can't understand that...
Arrays in Javascript (and most other languages) are passed by reference.
When you write add(myArray), you are passing a reference to the same Array instance that the global myArray variable refers to.
Any changes to the array object will be visible through both references.
To copy the actual array instance, write add(myArray.slice());.
Note that this will not copy the objects inside of it.
If you need to be able to nest arrays, then I'd change the .add() function to have the .concat() duplicate the Array into a variable, .push() the new value into the new Array, and return it.
function add(arr) {
var newArr = arr.concat(); // duplicate
newArr.push("e"); // push new value
return newArr; // return new (modified) Array
}
You could use concat() as well, and return the new array it creates.
var myArray = ["a", "b", "c", "d"];
function add(arr) {
return arr.concat("e");
}
var newArray = add(myArray);
console.log( newArray ); // ["a", "b", "c", "d", "e"]
console.log( myArray ); // ["a", "b", "c", "d"]
So instead of two methods .slice() then .push(), you accomplish it with one .concat().
This also gives you the benefit of passing another Array instead of a string, so:
return arr.concat(["e","f"]);
would give you:
// ["a", "b", "c", "d", "e", "f"]
instead of:
// ["a", "b", "c", "d", ["e", "f"] ]