Comparing equality of elements in two arrays - javascript

I have an assignment where I am supposed to check two arrays (unsorted) with integers, to see if
They have the same length
The first element contains integers and the second has the same values squared, in any order
For example:
test([5,4,1], [1,16,25]) // would return true ..
What I've done so far is first sort the two input arrays, and then compare the length. Once we confirm the length is the same we iterate through each value to make sure they're equal. Keep in mind I haven't gotten to comparing the values to their squared counterpart yet, because my loop is not giving me expected results. Here is the code:
function test(arr1, arr2){
// sort arrays
const arr1Sort = arr1.sort(),
arr2Sort = arr2.sort();
// compare length and then compare values
if(arr1Sort.length === arr2Sort.length) {
for(let i = 0; i < arr1Sort.length; i++) {
if(arr1Sort[i] === arr2Sort[i]) {
return true;
} else {
return false;
}
}
}
}
console.log(test([1,2,3], [1,5,4])); returns true but the array values are different?!

Inside the for, no matter whether the if or else is fulfilled, the function will immediately return true or false on the first iteration - it'll never get past index 0. To start with, return true only after the loop has concluded, and return false if arr1Sort[i] ** 2 !== arr2Sort[i] (to check if the first squared equals the second).
Also, when sorting, make sure to use a callback function to compare each item's difference, because otherwise, .sort will sort lexiographically (eg, [1, 11, 2]):
function comp(arr1, arr2){
// sort arrays
const sortCb = (a, b) => a - b;
const arr1Sort = arr1.sort(sortCb),
arr2Sort = arr2.sort(sortCb);
// compare length and then compare values
if(arr1Sort.length !== arr2Sort.length) {
return false;
}
for(let i = 0; i < arr1Sort.length; i++) {
if(arr1Sort[i] ** 2 !== arr2Sort[i]) {
return false;
}
}
return true;
}
console.log(comp([1,2,3], [1,5,4]));
console.log(comp([5,4,1], [1,16,25]));
You can decrease the computational complexity to O(N) instead of O(N log N) by turning arr2 into an object indexed by the squared number beforehand:
function comp(arr1, arr2){
if (arr1.length !== arr2.length) {
return false;
}
const arr2Obj = arr2.reduce((a, num) => {
a[num] = (a[num] || 0) + 1;
return a;
}, {});
for (let i = 0; i < arr1.length; i++) {
const sq = arr1[i] ** 2;
if (!arr2Obj[sq]) {
return false;
}
arr2Obj[sq]--;
}
return true;
}
console.log(comp([1,2,3], [1,5,4]));
console.log(comp([5,4,1], [1,16,25]));
(if duplicates weren't permitted, this would be a lot easier with a Set instead, but they are, unfortunately)

This should work, no mater the data to compare:
function similar(needle, haystack, exact){
if(needle === haystack){
return true;
}
if(needle instanceof Date && haystack instanceof Date){
return needle.getTime() === haystack.getTime();
}
if(!needle || !haystack || (typeof needle !== 'object' && typeof haystack !== 'object')){
return needle === haystack;
}
if(needle === null || needle === undefined || haystack === null || haystack === undefined || needle.prototype !== haystack.prototype){
return false;
}
var keys = Object.keys(needle);
if(exact && keys.length !== Object.keys(haystack).length){
return false;
}
return keys.every(function(k){
return similar(needle[k], haystack[k]);
});
}
console.log(similar(['a', {cool:'stuff', yes:1}, 7], ['a', {cool:'stuff', yes:1}, 7], true));
// not exact
console.log(similar(['a', {cool:'stuff', yes:1}, 7], ['a', {cool:'stuff', stuff:'more', yes:1}, 7, 'more stuff only at the end for numeric array']));

Related

Create a function to evaluate if all elements in the array are the same

Problem
I'm trying to create a function that evaluates an array and if every element inside the array is the same, it would return true and otherwise false. I don't want it to return true/false for each individual element, just for the entire array.
Attempt 1
This method works, but it returns true/false for each element in the array:
function isUniform(arr){
let first = arr[0];
for (let i = 1; i <arr.length; i++){
if (arr[0] !== arr[i]){
console.log(false);
} else {
console.log(true);
}
}
}
Attempt 2
This method returns true/false, once and then prints true again at the end:
function isUniform(arr){
let first = arr[0];
for (let i = 1; i <arr.length; i++){
if (arr[0] !== arr[i]){
console.log(false);
}
}
console.log(true);
}
If you want to test if something is true for every element of an array, you don't really need to write much — you can use array.every for this and just compare the first element. every() is nice because it will return early if a false condition is found.
var arr1 = [1, 1, 1, 1, 1, 1, 1]
var arr2 = [1, 1, 1, 2, 1, 1, 1]
console.log(arr1.every((n, _, self) => n === self[0]))
console.log(arr2.every((n, _, self) => n === self[0]))
This will return true for an empty array, which may or may not be what you want.
Alternative using the object Set
new Set(arr).size === 1 // This means all the elements are equal.
let isUniform = (arr) => new Set(arr).size === 1;
console.log(isUniform([4,4,4,4,4]));
console.log(isUniform([4,4,4,4,4,5]));
Add a return statement with false and end the function. The return value could be used later.
function isUniform(arr) {
let first = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[0] !== arr[i]) {
console.log(false);
return false;
}
}
console.log(true);
return true;
}
For using a return value, you need to return true at the end, too.
Try with Array#every .its Checking all other value is same with first index of array
function isUniform(arr) {
return arr.every(a=> a === arr[0])
}
console.log(isUniform([2,2,2,2]));
console.log(isUniform([4,4,4,4,4,5]));
The problem is that you need to stop once you've found the first false element:
function isUniform(arr){
let first = arr[0];
let uniform = true;
for (let i = 1; i <arr.length; i++){
if (arr[0] !== arr[i]){
uniform = false;
break;
}
}
console.log(uniform);
}

Comparing arrays in javascript where order doesn't matter

I am learning Javascript, and I saw a function on SO for comparing arrays to check if they are same. However, my current function returns false if the two arrays are [string1,string2] & [string2,string1]. Basically, the two positions are interchanged. The code is as follows:
function _compareArrays(arr1,arr2){
var result = arr1 != null && arr2 != null && arr1.length == arr2.length && arr1.every(function(element) {
return arr2.indexOf(element);
});
return result ;
}
However, I want these two arrays to be returned as same. So, I changed .every to .indexOf() and it seemed to work. But, I have a doubt, how exactly is the counter getting incremented here to make sure the comparison is being done for every element?
I mean, like, in C++, we do,
for (int i = 0; i < 10; i++)
if (arr1[i] == arr2[i]
cout<<"Elements are same\n";
Here, I have an explicit i++ which increments the counter. How does it happen in the above function?
Thanks!
Convert the array into an object instead. Convert array values into keys and their respective count as their value. This is more performant as you iterate over the arrays only once.
function compare(a, b) {
if (a.length !== b.length) {
return false;
}
let set = {};
a.forEach((i) => {
if (set[i] !== undefined) {
set[i]++;
} else {
set[i] = 1;
}
});
let difference = b.every((i) => {
if (set[i] === undefined) {
return false;
} else {
set[i]--;
if (set[i] === 0) {
delete set[i];
}
return true;
}
});
return Object.keys(set) == 0 && difference;
}
The first loop on the first array initialises the the set (object), the second loop on the second array subtracts the count and removes the keys when the count hits 0. If a key is not found or if the set is not empty at the end of the procedure, then the arrays are not similar.
Your current has these issues:
It will return true for these 2 arrays (I hope that you understand that this is not specific to these 2): [1, 2, 2], [1,1,2]. This problem is why I set indices to undefined after a match in my solution below.
indexOf returns -1 for element's it cannot find, and any number >= 0 if it can find the element, -1 is truthy, so if an element cannot be found, your comparison will return true, and 0 is falsy, so if an element is located in the first index of the second array, your method will return false (which means the whole thing will become false, because of every...). So, you should add a ~ before the result of indexOf call: ~arr2.indexOf(element).
See this
MDN page on the Bitwise Not operator (~) and how it solves the
indexOf problem I mentioned.
I also recommend that you take a look at this answer of mine on truthy/falsy values and how they interact with && and ||.
So Try this out (it's mostly your example, except there is no indexOf used and I have fixed problem #2):
function _compareArrays(arr1,arr2){
if(!(arr1 != null && arr2 != null && arr1.length == arr2.length)) {
return false;
}
/* copy the arrays so that the original arrays are not affected when we set the indices to "undefined" */
arr1 = [].concat(arr1);
arr2 = [].concat(arr2);
return arr1.every(function(element, index) {
return arr2.some(function(e, i) {
return e === element && (arr2[i] = undefined, true);
});
});
}
var x = ["str", "boo", "str"];
var y = ["boo", "str", "str"];
var z = ["abc", "def", "ghi"]
console.log(_compareArrays(x, y));
console.log(_compareArrays(x, z));
console.log(_compareArrays(z, z));
It won't work if the array has any undefined elements, though.
So the fastest way would be to sort both arrays, then compare each one element by element. This requires 2n * log(n) + n time rather than n2 time.
function compareArrays(arr1, arr2){
if(arr1.length !== arr2.length) return false;
// implement custom sort if necessary
arr1.sort();
arr2.sort();
// use normal for loop so we can return immediately if not equal
for(let i=0; i<arr1.length; i++){
if(arr1[i] !== arr2[i]) return false;
}
return true;
}

Mixed Element Small Number - JS

Hi I am creating a function that returns the smallest number within the given array. If the array contains no numbers, it should return 0.
Here's my function:
function findSmallestNumberAmongMixedElements(arr){
if(arr.length === 0 && typeof arr === 'string'){
return 0;
} else{
return Math.min.apply(null, arr); //min=1
}
}
var output = findSmallestNumberAmongMixedElements([4, 'lincoln', 9, 'octopus']);
console.log(output); // --> 4
Right now my answer return NAN instead of 4. Do you have any idea what am I doing wrong?
typeof arr === "string" will always be false if you are passing an array (wether or not the array contain strings is irrelevant for that test).
What you should do is first, filter the numbers out of the array using filter, then call Math.min on the filtered array:
function findSmallestNumberAmongMixedElements(arr) {
var onlyNumbers = arr.filter(e => typeof e === "number"); // filter out only items that are numbers
if(onlyNumbers.length === 0) return 0; // if there is no numbers, return 0
return Math.min.apply(null, onlyNumbers); // otherwise return the min of them
}
var output = findSmallestNumberAmongMixedElements([4, 'lincoln', 9, 'octopus']);
console.log(output); // --> 4
Your current function may produce NaN when you try to apply Math.min to an input array which contains both string and number values. Math.min can only handle numeric inputs, so the presence of a string may cause it to return the NaN error value.
There is a simple solution: filter out all of the non-number values from the array. We can check the length of the filtered array to see if there were any non-numeric values and, if so, apply Math.min without worry.
function findSmallestNumberAmongMixedElements(arr) {
var filtered = arr.filter(function(el) {
return typeof el == 'number';
});
if (filtered.length > 0) {
return Math.min.apply(Math, filtered);
} else {
return 0;
}
}
[
[4, 'lincoln', 9, 'octopus'], // 4
[], // 0
['a', 3, 2], // 2
['h', 'i'], // 0
['-3', 3], // 3
].forEach(function(input) {
console.log(input, findSmallestNumberAmongMixedElements(input));
});
In your Math.min.apply(null, arr), you are getting NaN because there are strings in the array.
Your typeof arr === 'string' does not loop through your array to exclude string value within the array.
What you need to do to achieve your goal is to probably just do a for loop or forEach loop:
function findSmallestNumberAmongMixedElements(arr){
var min = Infinity;
// if argument is not array or array has no value
if (arr.constructor !== Array || arr.length === 0){
min = 0;
return min;
}
/* for loop */
for (var i = 0, len = arr.length; i < len; i++){
if (arr[i] === 0 || typeof arr[i] !== 'number') continue;
min = Math.min(min, arr[i]);
}
/* forEach loop */
arr.forEach(function(value, index){
if (value === 0 || typeof value !== 'number') return;
min = Math.min(min, value);
});
return min;
}
var output = findSmallestNumberAmongMixedElements([4, 'lincoln', 9, 'octopus']);
console.log(output); // --> 4
I think you should filter your array before giving it to Math.min
Try the following
function isNumber (obj) {
return obj!== undefined && typeof(obj) === 'number' && !isNaN(obj);
}
function smallestInMixedArray (arr) {
if (arr.length === 0 || !arr.filter) {
return 0;
} else {
arr = arr.filter(isNumber)
return Math.min.apply(null, arr); //min=1
}
}
var output = smallestInMixedArray([4, 'lincoln', 9, 'octopus']);
console.log(output); // --> 4

JavaScript: How to match out-of-order arrays

I'm trying to work out how to match arrays that share the same elements, but not necessarily in the same order.
For example, these two arrays share the same set of elements, even though they're in a different order.
Is there any way to determine whether two arrays contain the same elements?
var search1 = ["barry", "beth", "debbie"];
var search2 = ["beth", "barry", "debbie"];
if (search1 == search2) {
document.write("We've found a match!");
} else {
document.write("Nothing matches");
}
I've got a Codepen of this running at the moment over here: http://codepen.io/realph/pen/grblI
The problem with some of the other solutions is that they are of O(n²) complexity, if they're using a for loop inside of a for loop. That's slow! You don't need to sort either—also slow.
We can speed this up to O(2n) complexity1 by using a simple dictionary. This adds O(2n) storage, but that hardly matters.
JavaScript
var isEqual = function (arr1, arr2) {
if (arr1.length !== arr2.length) {
return false; // no point in wasting time if they are of different lengths
} else {
var holder = {}, i = 0, l = arr2.length;
// holder is our dictionary
arr1.forEach(function (d) {
holder[d] = true; // put each item in arr1 into the dictionary
})
for (; i < l; i++) { // run through the second array
if (!(arr2[i] in holder)) return false;
// if it's not in the dictionary, return false
}
return true; // otherwise, return true
}
}
Test Case
var arr1 = ["barry", "beth", "debbie"],
arr2 = ["beth", "barry", "debbie"];
console.log(isEqual(arr1,arr2));
// returns true
fiddle
Improvement
As Ahruss pointed out, the above function will return true for two arrays that are seemingly equal. For example, [1,1,2,3] and [1,2,2,3] would return true. To overcome this, simply use a counter in the dictionary. This works because !undefined and !0 both return true.
var isReallyEqual = function (arr1, arr2) {
if (arr1.length !== arr2.length) {
return false; // no point in wasting time if they are of different lengths
} else {
var holder = {}, i = 0, l = arr2.length;
// holder is our dictionary
arr1.forEach(function (d) {
holder[d] = (holder[d] || 0) + 1;
// checks whether holder[d] is in the dictionary: holder[d] || 0
// this basically forces a cast to 0 if holder[d] === undefined
// then increments the value
})
for (; i < l; i++) { // run through the second array
if (!holder[arr2[i]]) { // if it's not "in" the dictionary
return false; // return false
// this works because holder[arr2[i]] can be either
// undefined or 0 (or a number > 0)
// if it's not there at all, this will correctly return false
// if it's 0 and there should be another one
// (first array has the element twice, second array has it once)
// it will also return false
} else {
holder[arr2[i]] -= 1; // otherwise decrement the counter
}
}
return true;
// all good, so return true
}
}
Test Case
var arr1 = [1, 1, 2],
arr2 = [1, 2, 2];
isEqual(arr1, arr2); // returns true
isReallyEqual(arr1, arr2); // returns false;
1: It's really O(n+m) complexity, whereby n is the size of the first array and m of the second array. However, in theory, m === n, if the arrays are equal, or the difference is nominal as n -> ∞, so it can be said to be of O(2n) complexity. If you're feeling really pedantic, you can say it's of O(n), or linear, complexity.
you can use this function to compare two arrays
function getMatch(a, b) {
for ( var i = 0; i < a.length; i++ ) {
for ( var e = 0; e < b.length; e++ ) {
if ( a[i] === b[e] ){
return true;
}
}
}
}
Feed your arrays to the following function:
function isArrayEqual(firstArray, secondArray) {
if (firstArray === secondArray) return true;
if (firstArray == null || secondArray == null) return false;
if (firstArray.length != secondArray.length) return false;
// optional - sort the arrays
// firstArray.sort();
// secondArray.sort();
for (var i = 0; i < firstArray.length; ++i) {
if (firstArray[i] !== secondArray[i]) return false;
}
return true;
}
Now you may be thinking, can't I just say arrayOne.sort() and arrayTwo.sort() then compare if arrayOne == arrayTwo? The answer is no you can't in your case. While their contents may be the same, they're not the same object (comparison by reference).
You need to simply sort them, then compare them
function compareArrayItems(array1, array2){
array1 = array1.sort();
array2 = array2.sort();
return array1.equals(array2);
}
fiddle
You can use the equals function provided in How to compare arrays in JavaScript?
Sort them firstly. Secondly, if their length is different, then they're not a match.
After that, iterate one array and test a[i] with b[i], a being the first array, b the second.
var search1 = ["barry", "beth", "debbie"],
search2 = ["beth", "barry", "debbie"];
// If length are different, than we have no match.
if ((search1.length != search2.length) || (search1 == null || search2 == null))
document.write("Nothing matches");
var a = search1.sort(),
b = search2.sort(),
areEqual = true;
for (var i = 0; i < a.length; i++) {
// if any two values from the two arrays are different, than we have no match.
if (a[i] != b[i]) {
areEqual = false;
break; // no need to continue
}
}
document.write(areEqual ? "We've found a match!" : "Nothing matches");

JavaScript - Compare two multidimensional arrays

I have two multidimensional arrays:
first is something like (['one','one','three'],['four','five',five'],['one','one','one'])
and the second one is like this (['one','one','nine'],['one','one','one'],['two','two'],['two','two','two']...)
Now, what I want is to find match first index of first array with second array, but position of at least first two indexes from boths array must match also, eg.:
first_array (['one','one','three'],['four','five',five'],['one','one','one'])
will match
second_array (['one','one','nine'],['one','one','one'],['two','two']['two','two','two']...)
and output would be eg. 'alert('Match.').
I have tried
for(i=0; i<1; i++){
if(first_array[0] == second_array) console.log('Match');
else console.log('No match');
}
but I constantly get 'No match' although there is a match.
P.S. in 'for' loop, my i is i<1 because I want to compare only first index of first_array with complete second_array.
Thanks in advance
var md1 = [['one','one','three'],['four','five','five'],['one','one','one']];
var md2 = [['one','one','nine'],['one','one','one'],['two','two'],['two','two','two']];
//Iterate through all elements in first array
for(var x = 0; x < md1.length; x++){
//Iterate through all elements in second array
for(var y = 0; y < md2.length; y++){
/*This causes us to compare all elements
in first array to each element in second array
Since md1[x] stays fixed while md2[y] iterates through second array.
We compare the first two indexes of each array in conditional
*/
if(md1[x][0] == md2[y][0] && md1[x][1] == md2[y][1]){
alert("match found");
alert("Array 1 element with index " + x + " matches Array 2 element with index " + y);
}
}
}
Working Example http://jsfiddle.net/2nxBb/1/
Possible duplicate of How to compare arrays in JavaScript?.
For a strict array comparison, check their length and values like so:
var a1 = [1, 2, 3];
var a2 = [1, 2, 3];
array_compare(a1, a2);
function array_compare(a1, a2) {
if(a1.length != a2.length) {
return false;
}
for(var i in a1) {
// Don't forget to check for arrays in our arrays.
if(a1[i] instanceof Array && a2[i] instanceof Array) {
if(!array_compare(a1[i], a2[i])) {
return false;
}
}
else if(a1[i] != a2[i]) {
return false;
}
}
return true;
}
2 way, more simple if this enough for u
JSON.stringify(tree1) === JSON.stringify(tree2)
if not, use this: recursively handles multidimensional arrays and objects
treesAreSame(tree1, tree2) {
if (tree1 === tree2) {
return true;
}
if (tree1 === null || tree1 === undefined || tree2 == null) {
return false;
}
if (Array.isArray(tree1) !== Array.isArray(tree2)) {
return false;
}
if (tree1.length !== tree2.length) {
return false;
}
if (isArray(tree1)) {
for (let i = 0; i < tree1.length; ++i) {
let t1 = tree1[i];
let t2 = tree2[i];
if (isArray(t1) || isObject(t1)) {
if (!treesAreSame(t1, t2)) {
return false;
}
} else {
if (t1 !== t2) {
return false;
}
}
}
}
if (isObject(tree1)) {
for (const k of Object.keys(tree1)) {
let t1 = tree1[k];
let t2 = tree2[k];
if (isArray(t1) || isObject(t1)) {
if (!treesAreSame(t1, t2)) {
return false;
}
} else {
if (t1 !== t2) {
return false;
}
}
}
}
return true;
};
isObject(a) {
return (!!a) && (a.constructor === Object);
};
isArray(a) {
return (!!a) && (a.constructor === Array);
};

Categories

Resources