What would be the best approach to check if all the elements of a given array are contained by another array? For example:
match(['countryaarea', 'countrybarea'], ['countrya', 'countryb']) // true
I have tried indexOf() and includes(), but it does not work.
You can use every and some methods.
const arr = ['countryaarea', 'countrybarea'];
function match(array, searchArray) {
if (array.length !== searchArray.length) {
return false;
}
return array.every(el => searchArray.some(sEl => el.includes(sEl)));
}
console.log(match(arr, ['countrya', 'countryb'])); // returns true
console.log(match(arr, ['countrya'])) // returns false
console.log(match(arr, ['abcd'])) // returns false
console.log(match(arr, [])) // returns false
You would also want to check for null values.
... best approach...
The 'best approach' is robust code that handles edge cases. But the OP is insufficient to know what the edge cases are. That's addressed below but first, the code.
function isSubset ( subset, reference ) {
if ( !Array.isArray( subset ) || !Array.isArray( reference )) return false;
return subset.every( element => reference.includes( element ));
}
The 'best approach' for coding:
generally almost never involves for loops. Less readable and more error prone than Iterator functions. And as seen in the other answers, for loops put artificial constraints on the problem.
Code composition using functions is in keeping with Javascript language design, functional programming paradigm, OO principles, and clean code in general.
The 'best approach' depends on desired functionality:
Must the arrays be the same length? All answers (as of this writing) assumes that.
Must the test-array be shorter and/or equal length of the reference array?
Can either or both arrays have duplicate items?
Is this throw-away code ?
Is this destined for a library or at least production code?
What if there are mixed types in either or both arrays?
If this is simply "git er done", isolated use then verbose-but-understood code is OK and non-robust parameter checking is OK.
My assumptions
Return false for failed parameter checks, don't just blow up.
Both things must be arrays.
No constraints on array length.
Duplicate elements in either array do not need item-for-item duplication in the other.
An empty array is a subset of any other array including another empty array.
String compare is case sensitive.
I did not want to get wrapped around the axle with using "truthiness" for parameter validation so I left that sleeping demon alone.
var arraysMatch = function (arr1, arr2) {
// Check if the arrays are the same length
if (arr1.length !== arr2.length) return false;
// Check if all items exist and are in the same order
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
// Otherwise, return true
return true;
};
I'm working on a JavaScript exercise and having some trouble untangling the logic as to why it works. It basically has a function called "mystery" that uses a bunch of very simple functions, and returns an array you give it but in reversed order. I've been sitting in front of a whiteboard for an hour trying to figure out the logic behind it, and not getting it. Can a kind soul take a look at these functions and explain how the mystery function returns a reversed array? Thank you!
function rest(arr) {
return arr.slice(1);
}
function first(arr) {
return arr[0];
}
function conj(arr, value) {
arr.push(value);
return arr;
}
function mystery(array) {
if (array.length === 0) {
return [];
}
return conj(mystery(rest(array)), first(array));
}
mystery is a recursive function.
It calls itself using the return value of the rest function, which returns everything except the first element.
It uses the result of that + the result of first, which returns the first character, and concatenates them again (using conj), but with the first element at the end.
So, say you put in [H e l l o],
it will return conj(mystery([e l l o], H)
mystery([e l l o]) will return conj(mystery([l l o], e)
mystery([l l o]) will return conj(mystery([l o], l)
and so on, until the array that goes into mistery is empty, in which case the recursion ends, and we bubble back up to the first call.
Side note, recursion is often used for exercises like this, but although it has some specific uses, in many cases it's way more efficient to not use recursion, because the overhead of making another function call is relatively hard work, compared to other solutions using a simple loop to move or swap items around.
You can see what is happening if you output some information:
function rest(arr) {
return arr.slice(1);
}
function first(arr) {
return arr[0];
}
function conj(arr, value) {
arr.push(value);
return arr;
}
function mystery(array, level) {
if (array.length === 0) {
console.log('mystery level '+level+' is called with an empty array. Recursion ends here.. Stay tuned for the answer.');
return [];
}
console.log('mystery level '+level+' is called with '+array+
'. I will move '+first(array)+' to the end.');
var result = conj(mystery(rest(array), level+1), first(array));
console.log('returning '+result+' for level '+level);
return result;
}
console.log(mystery(['H','e','l','l','o'], 0));
To understand a function that uses recursion it can help to just assume for a moment that the recursive (nested) call returns what it should and then see how it builds on that to produce a correct result.
Let's for example suppose that array is [1, 2, 3, 4]
So this line:
conj(mystery(rest(array)), first(array));
... has a recursive call of mystery. It gets as argument the array, but with the first element removed from it (that is what rest returns), so it gets [2, 3, 4]
Now we will just assume that this recursive call of mystery does the right thing and reverses that array into [4, 3, 2]. Then in the above quoted code we see this result is concatenated with first(array) (which is the first value, i.e. 1). So we get [4, 3, 2, 1]. Correct!
This teaches us that if we assume mystery does the job right for an array with n-1 values, it also does it right for n values.
Now remains to see whether mystery deals correctly with the smallest case, i.e. when the array is empty. It is easy to see it returns the correct result in that case, i.e. an empty array.
So putting those two things together you can see that mystery does the job correctly for all possible array sizes.
your first function "rest" removes the first element, as slice will return elements from 1 to the end of the array, then the "conj" function will take the first element that was removed (through the "first" function) and put it in the end, and doing so recursively it'll take elements from the beginning and put them to the end.
The .push method places the item to end of array.
.slice(1) means “except the first item”
Pseudocode
Get array A (arg of mystery). If it is empty, return it
Take rest (everything except the first). We will call the rest B
Run this program on B (recursively)
Append the first item of A to end of B
conj = append value to arr
first = get first item of arr
rest = return everything except the first item
mystery when array is empty = return empty array
mystery when array is not empty = Take rest(array), run mystery on it, then append first of array
Yeah, the magic of recursion. To understand think about what it does if you call mystery with a 2-element array [1,2].
rest(array) will then be [2] mystery(rest(array)) will also be [2]
first(array) will be 1.
Then you return conj([2], 1) which locically results in [2,1].
Now the trick is the recursion. If you have 3 elements [0,1,2] and call mystery with it this will happen:
it will call mystery(rest(array)) with essentially is mystery([1,2]). That this returns [2,1] have we already seen.
first(array) will be 0
so it returns conj([2,1],0) which is logically [2,1,0].
this now recusivly works for as many elements as you wish. Essentially mystery will be called for every element to place it after all elements.
One thing to note here is that mystery() is called recursively. I added some comments to illustrate what's going on on each step.
function rest(arr) {
console.log('take first n-1 elements of', arr);
return arr.slice(1);
}
function first(arr) {
return arr[0];
}
function conj(arr, value) {
arr.push(value);
console.log('combine', arr, 'with', value)
return arr;
}
function mystery(array) {
console.log('call mystery on ', array)
if (array.length === 0) {
return [];
}
return conj(mystery(rest(array)), first(array));
}
mystery([0,1,2])
It's pretty simple. The reason for you not being able to see it is RECURSION. Couple things, that I would like you to notice:
mystery(array) is recursive function because it call itself until the array passed in is emplty
all of your work is happening here: return conj(mystery(rest(array)), first(array));
I'm not going to talk more about recursive function here rather I will show how you can track each recursive call using console.log(). Check out my code below, I've added console.log() to make things more clear for you. Try running mystery with some array and see results. This will make sense to you.
function rest(arr) {
return arr.slice(1);
}
function first(arr) {
console.log("Returning ",arr[0], "from first(arr).")
return arr[0];
}
function conj(arr, value) {
console.log("Pushing ",value, " to ",arr, " in conj(arr,value)");
arr.push(value);
console.log("Returning ",arr, " from Conj(arr,value)");
return arr;
}
function mystery(array) {
if (array.length === 0) {
console.log("array is emplty. So, returning empty array from mystery");
return [];
}
console.log("array is not emplty. So, calling mystery(array) again.");
return conj(mystery(rest(array)), first(array));
}
var reverse =mystery([1,2,3,4]);
console.log("The final result", reverse);
Not gonna lie, this is a weird way to reverse the order of the array. It's basically using recursion to slice the first element of the array and conjoin the elements with first element at the end.
So here's a walk though:
mystery(['h','e','l','l','o']) ->
first check if array is empty then method rest is called ->
rest(['h','e','l','l','o']) ->
rest slices the array at index 1 returning a new array of ['e','l','l','o'] ->
then mystery is called which repeats the previous steps with the return value of rest ->
mystery(['e','l','l','o']) ->
this means the mystery will be keep getting called on until rest returns an empty array ->
mystery(['e','l','l','o']) ->
mystery(['l','l','o']) ->
mystery(['l','o']) ->
mystery(['o']) ->
mystery([]) ->
when mystery has an empty array it returns the empty array and then first is called ->
first returns just the first element of the array and happens after mystery is returned it would look something like this before mystery is returned ->
mystery(['e','l','l','o']) , first(['h','e','l','l','o']) ->
so at the lowest level when mystery returns an empty array and first return the first element in ['o'] it would look like this ->
[] , ['o'] ->
conj would be called with these values ->
conj([] , ['o']) ->
conj returns the combination of the two values ->
conj([] , ['o']) ->
['o'] ->
this is then returned and repeated ->
conj([] , ['o']) ->
conj(['o'] , ['l']) ->
conj(['o','l'] , ['l']) ->
conj(['o', 'l','l'] , ['e']) ->
conj(['o', 'l','l', 'e'] , ['h']) ->
['o', 'l','l', 'e','h']
Trying to create "some" function. i.e., return true if any of the elements in teh array satisfies the condition
function some(array, predicate) {
for (var i = 0; i < array.length; i++) {
if (predicate(array[i]))
return true;
}
return false;
}
console.log(some([NaN, 3, 4], isNaN));
// → true
console.log(some([2, 3, 4], isNaN));
// → false
Question 1: Above is the solution in the book and I'm not sure why "return false" would overwrite "return true" in every case. The above solution seems to suggest that "return false" will be run only if "return true" was never run – WHY??
My solution looked like this.
var some = function(array, condition) {
var answer = false;
for(var i=0; i<array.length; i++) {
if(condition(array[i])) {answer = true};
}
return answer
};
Question 2: Is the book solution better because it takes less memory (ie the "answer" variable)?
Thanks!
return controls where the execution pointer is located for the currently executing script. When return is encountered it moves the execution pointer out of the function, and as a result, the function will stop executing. This means that if return true is executed, return false will not be.
The solution in the book is predicated on the fact that the return keyword used within the body of a for loop will cause the loop to stop execution and return.
Take a look at the at the Interrupt a function section of the return documentation, which states:
The return statement ends function execution and specifies a value to
be returned to the function caller.
In your example, as soon as the condition is matched, the book's solution will no longer continue to iterate through the array and will return (since that's what return does per the definition above).
Let's look at your solution:
var some = function(array, condition) {
var answer = false;
for(var i=0; i<array.length; i++) {
if(condition(array[i])) {answer = true};
}
return answer
};
What happens if the condition is met? You're setting the answer to true, but the loop will continue to check subsequent elements in the array.
Consider the use case where the array is very large and you want to find a specific value within the array.
Which do you think would be more efficient: the one that stops after it finds the match or the one that returns the match only after having looking at all elements?
In this case, the answer is the latter. Don't keep looping through all elements if you've already found what you need.
When the execution hits a return statement, it leaves the current function regardless of which block it was in within that function.
As soon as an array element corresponds to the predicate, it returns true. If the execution exits the for loop normally, it means not a single value was valid, so we can return false.
The book's answer is better, not because it doesn't create an extra variable (that doesn't matter), but because it doesn't check all variables every time. As soon as an array element is valid, it exits the function.
You can use the built in method of Array, some():
var test = [NaN, 3, 4].some(isNaN); // true
What that function does is
function some(array, predicate) { // return a boolean value (true, false)
// as soon an Array key matches `predicate`
for (var i = 0; i < array.length; i++) {
if (predicate(array[i])) return true; // if Array key isNaN exit function as `true`
}
// We're 1. already out of the function or 2. all keys are looped.
// This function needs to return a boolean so exit with `false`
// since all Array keys are Numbers
return false;
}
the above explains it all. Remember the part that says:
"return a boolean value (true, false) as soon an Array key matches predicate"
In your example, you're not exiting the function, so if you passed for example an
array of 1000 keys, every single key would be looped, which the function is not meant to do since it's purpose it to:
Return (exit) as true if at least one key matches the predicate condition
looping all other keys is useless since we already discovered that one key matches.
Yes, var consumes interpreter memory (nothing to worry about).
I am trying to create a function that will trim off array values that are not 4 chars long. For some reason, it does not work. I think that it might be with the return statement but I am not sure and I need help with it. Here is the code for the function: (Please don't suggest ways of making the hasher better, I just need help on the function)
function cutit(seq){
for(var i=0;i<seq.length;i++){
var temparr=seq[i].split("");
if(temparr.length % 4 !== 0){
seq.splice(seq[i],1);
return seq;
}
}
}
Five things:
Yes, the return should happen after the loop not after the first found item to delete.
.splice() takes an index for the first parameter, not the element. Pass i instead of seq[i].
splice(…, 1) does decrease the length of the array you're iterating over by one. You need to take care of that and decrease your counter as well to have a look at the i index again, where on the next iteration the next element will sit.
Instead of splitting the string into an array of single chars and taking the length of that, just take the length of the string directly.
Your functions removes all strings that are not a multiple of 4 long, not all that are not exactly 4 characters long. Not sure whether that is intended.
function cutit(seq) {
for (var i=0; i<seq.length; i++) {
if (seq[i].length !== 4) {
seq.splice(i--, 1);
}
}
return seq;
}
Also, notice that mutating an array is seldom a good idea (especially in an inefficient way like this with multiple splices). Returning a new array is so much easier, and you can use the higher-order filter Array method for that:
function cutit(seq) {
return seq.filter(function(str) {
return str.length === 4;
});
}
Does the ForEach loop allow us to use break and continue?
I've tried using both but I received an error:
Illegal break/continue statement
If it does allow, how do I use them?
No, it doesn't, because you pass a callback as a return, which is executed as an ordinary function.
Let me be clear:
var arr = [1,2,3];
arr.forEach(function(i) {
console.log(i);
});
// is like
var cb = function(i) {
console.log(i);
// would "break" here do anything?
// would "continue" here do anything?
// of course not.
}
for(var j = 0; j < arr.length; j++) {
cb(arr[j]);
}
All forEach does is call a real, actual function you give to it repeatedly, ignore how it exits, then calls it again on the next element.
In that callback function if you return it will incidentally work like continue in the case of forEach. Because the rest of the function won't execute that time, and it will be called again. There is no analogue of break.
Ruby supports this flexibility, using blocks/procs instead of methods/lambdas.
Per Mozilla's documentation:
Note : There is no way to stop or break a forEach loop. The solution is to use Array.every or Array.some. See example below.
every and some are exactly like forEach, except they pay attention to the return value of the callback. every will break on a falsey value and some will break on a truthy value, since they shortcircuit. They are exactly analogous to && and ||. && tests whether every expression is truthy, || tests for whether some is, which should help you remember how short-circuiting works with every and some. When in doubt, try it.
As already answered, you cannot use continue or break inside a JavaScript Array.prototype.forEach loop. However, there are other options:
Option 1
Use the jQuery .each() function (Reference).
Simply return true to continue, or return false to break the loop.
Option 2
Just use return to continue or throw new Error() to break. I don't necessarily recommend doing it this way, but it's interesting to know that this is possible.
try {
[1, 2, 3, 4].forEach(function(i) {
if (i === 2) {
return; // continue
}
if (i === 3) {
throw new Error(); // break
}
console.log(i);
});
}
catch (e) {
}
The expected result is just 1