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;
}
Related
Write a function getDuplicates that returns an array of all the elements that appear more than once in the initial items array (keeping the order). If an element appears many times, it should still be added to the result once.
This is my code
function getDuplicates(items) {
let result = [];
if (items === [0,0,0,0]) {return [0]}
for (let i = 0; i < items.length; i++) {
for (let j = i + 1; j < items.length; j++) {
if (items[i] === items[j]) {
result.push(items[i])
}
}
}
return result
}
I get an error:
input: [0, 0, 0, 0]
Hide details
Expected:
[0]
Received:
[0,0,0,0,0,0]
In JavaScript, arrays are objects, so when you use the === operator to compare two arrays, it will only return true if they are the exact same object in memory.
Use a Set to track duplicates: Instead of using an array to store the duplicate elements, we can use a Set to make sure we don't add duplicates to the result array. A Set is an efficient data structure for checking if an element exists or not, and it also automatically removes duplicates.
Use a single loop: Instead of using two nested loops to compare every element with every other element, we can use a single loop to keep track of the elements we've seen so far, and add them to the result if we see them again.
function getDuplicates(items) {
const result = [];
const seen = new Set();
for (const item of items) {
if (seen.has(item) && !result.includes(item)) {
result.push(item);
} else {
seen.add(item);
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
a modified version of yours
function getDuplicates(items) {
let result = [];
let added = {};
for (let i = 0; i < items.length; i++) {
if (!added[items[i]] && items.indexOf(items[i], i + 1) !== -1) {
result.push(items[i]);
added[items[i]] = true;
}
}
return result;
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
or in short doing the same
const getDuplicates = items => items.filter((item, index) => items.indexOf(item) !== index && items.lastIndexOf(item) === index);
console.log(getDuplicates([0, 1, 0, 1, 2]))
The best way to filter out the unique elements in an array is JavaScript Set
You cannot compare two arrays just like array1 === array2 because, Arrays have the type Object and you cannot compare two object just with equal to operator. Objects are not compared based on their values but based on the references of the variables. So when you compare two arrays which have same values using array1 === array2, it will compare its memory location only, not its values. So it will be only false.
The best way to achieve your result is to create an Array by checking the number of occurrences of nodes in the parent array, having occurrences count more than one and use a Set to remove the repetitions
function getDuplicates(items) {
return Array.from(new Set(items.filter(node => items.filter(x => node === x).length > 1)))
}
console.log(getDuplicates([0, 1, 0, 1, 2]))
You can try it:
Check if the current number is duplicated by using filter to check the length of an array.
Check if the result array contains duplicates.
function getDuplicates(items) {
let result = [];
for (let i = 0; i < items.length; i++) {
if ((items.filter(item => item == items[i])).length > 1 && !result.includes(items[i])) {
result.push(items[i]);
}
}
return result;
}
console.log(getDuplicates([0, 0, 0, 0]));
So. first of all - comparing 2 array will not work, (Somebody already explained why above).
Your code doesn't work because of if statement. You're checking if an array doesn't have any value except 0.
Try summing all numbers in the array and check if it's 0.
if(arr.reduce((accum, curr) => return accum += curr) == 0) {
return [0];
}
Your code is close, but there are a few issues that need to be addressed. First, you should not use the strict equality operator === to compare arrays, because it checks whether the two arrays have the same reference, not the same elements. Instead, you can use the JSON.stringify() method to compare the string representations of the arrays.
Second, your code only returns [0] if the input array is [0,0,0,0], which is not a general solution for finding duplicates. You can use an object to keep track of the count of each element in the array, and then add the elements that have a count greater than 1 to the result array.
Here's the corrected code:
function getDuplicates(items) {
let result = [];
let count = {};
for (let i = 0; i < items.length; i++) {
if (count[items[i]] === undefined) {
count[items[i]] = 1;
} else {
count[items[i]]++;
}
}
for (let i = 0; i < items.length; i++) {
if (count[items[i]] > 1 && result.indexOf(items[i]) === -1) {
result.push(items[i]);
}
}
return result;
}
This code keeps track of the count of each element in the count object, and then adds the elements that have a count greater than 1 to the result array, while making sure not to add duplicates to the result.
I'm fairly new to coding and enlisted for the daily coding problem mailing list and got this question:
Given a list of numbers and a number k, return whether any two numbers
from the list add up to k.
My solution (after some stackoverflow digging) looks like this;
function problemOne_Solve()
{
const k = 17;
const values = [11, 15, 3, 8, 2];
for (i=0; i < values.length; i++) {
if ( values.find( (sum) => { return k-values[i] === sum} ) ) return true;
}
return false;
}
I'm wondering why it works. To me it looks like the part with the fat-arrow function closes the brackets inside the if statements conditional logic. And there is no such brackets after the if statement, which I thought was required.
I was also wondering how i would go about outputting the pair or pairs that sums up to "k," to build further on the solution. I would like to be able to display the pairs on the page for example.
.find takes a callback, which is invoked for every item in the array (or, up until a match is found). The first argument to the callback is the item being iterated over. If a match is found (if the return value from the callback was truthy for any element), the .find returns the item that resulted in a truthy return value.
So, on the first i = 0 iteration, and values[i] is 11, (sum) => { return k-values[i] === sum} will first check whether 17 - 11 === 11, and then whether 17 - 11 === 15, and then whether 17 - 11 = 3, etc.
This condition will generally be fulfilled if two numbers in the array add up to the k, but the algorithm is buggy. For example, an array composed of [1] will check the 1 against itself on the first iteration, adding up to 2:
function problemOne_Solve() {
const k = 2;
const values = [1];
for (i=0; i < values.length; i++) {
if ( values.find( (sum) => { return k-values[i] === sum} ) ) return true;
}
return false;
}
console.log(problemOne_Solve());
That is wrong. Another problem is that .find returns the found value. But, if the array is an array of numbers, the found value may be 0, and 0 is falsey. So the below example should return true because two elements sum up to 0 (0 and 0), but it returns false:
function problemOne_Solve() {
const k = 0;
const values = [0, 0];
for (i=0; i < values.length; i++) {
if ( values.find( (sum) => { return k-values[i] === sum} ) ) return true;
}
return false;
}
console.log(problemOne_Solve());
To get it right and decrease the computational complexity from O(n ^ 2) to O(n), iterate over the array once. Create an object whose keys are the numbers being iterated over, and on each iteration, check to see if a key of target - currNum exists on the object (where target is the target sum, and currNum is the current number from the array):
function problemOne_Solve() {
const target = 17;
const values = [11, 15, 3, 8, 2];
const obj = {};
for (const currNum of values) {
if (obj.hasOwnProperty(target - currNum)) {
return true;
}
obj[currNum] = true;
}
return false;
}
console.log(problemOne_Solve());
I was also wondering how i would go about outputting the pair or pairs that sums up to "k," to build further on the solution. I would like to be able to display the pairs on the page for example.
Instead of returning immediately when a match is found, push to an array and then return that array at the end of the function. Also, instead of setting the object values to true (or false), set them to the number of occurrences the number has been found so far (and decrement the matching number when a match is found):
function problemOne_Solve() {
const target = 17;
const values = [11, 15, 3, 8, 2, 17, 0, 0, 17];
const obj = {};
const matches = [];
for (const currNum of values) {
const otherNum = target - currNum;
if (obj[otherNum]) {
obj[otherNum]--;
matches.push([currNum, otherNum]);
}
obj[currNum] = (obj[currNum] || 0) + 1;
}
return matches;
}
console.log(problemOne_Solve());
And there is no such brackets after the if statement, which I thought was required.
Brackets are not required when there's a single statement after an if (or else if or else), eg:
if (true) console.log('true');
else console.log('this will not log');
And there is no such brackets after the if statement, which I thought was required.
If there is only one statement after if else the brackets becomes optional. Ideally you shouldn't write the if block is one line to make your code clean
As you are a beginner I would recommend you to use simple for loops instead of these fancy methods like find.
You can do that in following steps:
Its clear that you need sum of each element with every other element of array. So you will need a nested loop structure
The outer loop or main loop starts from 0 and loop till end of array.
You need to create a inner loop or nested loop which starts from index after the current index.
In the each iteration of nested loop you need to check if the sum of two elements is equal to requiredSum or not.
function pairWithSum(givenArray, requiredSum){
for(let i = 0; i < givenArray.length; i++){
for(let j = i + 1; j < givenArray.length; j++){
let sum = givenArray[i] + givenArray[j];
if(sum === requiredSum){
return [givenArray[i], givenArray[j]];
}
}
}
return false
}
console.log(pairWithSum([1, 4, 5, 8], 12));
console.log(pairWithSum([1, 4, 5, 8], 15));
I'm wondering why it works
That is because, if expects an expression/ statement to validate.
values.find( (sum) => { return k-values[i] === sum} )
This is a statement and it will be evaluated before and its output will be passed to if for condition.
Now Array.find has a return type: T|<null> where T is any value array is made of. So in second iteration, when values[i] refers to 15, it will return 2.
Now in JS, 2 is a truthy value and hence it goes inside if block. Foe more reference, check All falsey values in JavaScript. Any value that is not in this list will be considered as true.
My Javascript solution using JS object. This solution memories the elements when we go through the array which can be memory expensive. But the complexity will stay as O(n).
const checkTwoSum = (arr, sum) => {
const obj = {};
const found = arr?.find(item => {
const target = sum - item;
if (obj[target]) return true;
else {
obj[item] = 1;
}
});
return !!(found || found === 0);
}
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']));
function nonUniqueElements(data) {
var duplicates = [];
var compArr = data;
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < compArr.length; j++) {
console.log('Comparing ' + data[i] + ' to ' + compArr[j]);
if (data[i] === compArr[j]) {
console.log('Found match first pass');
console.log(data.indexOf(i), compArr.indexOf(j));
if (data.indexOf(i) !== compArr.indexOf(j)) {
console.log('Also passes second pass')
console.log('Pushing ' + data[i] + ' to new array')
duplicates.push(data[i]);
console.log(duplicates);
}
}
}
console.log('End of run through');
}
return (duplicates);
}
console.log(nonUniqueElements([5, 5, 5, 5]));
I'm trying to return all non-unique values in an array. I've duplicated the array and am running a nested loop to compare the copy against the original.
When it finds a match (in this case every time), there's a second check to ensure that only values at a different index are pushed to a new array.
I've put some console.log()s to help me step through the program. Even when console.log(data.indexOf(i), compArr.indexOf(j)) is printing different values, the block of code in the if (data.indexOf(i) !== compArr.indexOf(j)) statement isn't running.
Any ideas?
In JS, the indexOf() method returns the position of the first occurrence of a specified value in a string/array. You can tell it's wrong since in the console.log you always get the value -1.
What you need to compare is data[i] and compArr[j].
if (data.indexOf(i) !== compArr.indexOf(j)) {
Why are you comparing indexes of i and j ?
i and j are indexes themselves, not elements.
Don't you mean
if (data[i] !== compArr[j]) {
You are trying to compare two true values.
Take a look at the definition of indexOf is:
Array.indexOf: method returns the first index at which a given element can be found in the array, or -1 if it is not present.
String.indexOf: method returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex. Returns -1 if the value is not found.
So, in your condition if (data.indexOf(i) !== compArr.indexOf(j)) you're comparing the index of i and j that holds the numbers of 0 to 3 (data.length). In your Array, all the values have been set to 5 witch is the reason that you get -1 in return of indexOf, [5,5,5,5].indexOf(0) === -1.
If you're trying to compare the values in data Array you have to compare the values not index of values. Like this: if (data[i] !== compArr[j]).
At the end I recommend you to read this document Truthy and Falsy: When All is Not Equal in JavaScript
It might be simpler to use Array.reduce...
var array = [1,2,3,4,5,6,4,5,66,44,66]
var result = array.reduce((prev,value) => {
if ( prev.exists[value] ) { prev.dupes[value] = true; }
prev.exists[value] = true;
return prev;
},{ exists: {}, dupes: {} })
var duplicates = Object.keys(result.dupes);
console.log('duplicates',duplicates);
var filterDupes = array.filter(value => result.dupes[value])
console.log('fitlerDupes',filterDupes);
var mapDupes = array.map(value => result.dupes[value] ? value : null)
console.log('mapDupes',mapDupes);
for ( var i = 0; i < array.length; i ++ ) {
if ( !result.dupes[array[i]] ) { array.splice(i,1); i--; }
}
console.log('Original Array',array);
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");