Optimize Time Complexity For Odd Occurrences In Array - javascript

I have this code that pairs same elements in an array, with the expectation that the array will have an odd length and it should return the only element that couldn't get a pair. So I wrote the code just well, and it works fine for smaller arrays, but with very large big integers of over 1 billion, the time complexity became O(N**2) and then the need to refactor my code to get a much better performance for large arrays and large array elements. Here is my code below;
function solution(A) {
if(!Array.isArray(A)) return 0;
var temp = new Array(A.length);
var position = 0;
for(let i=0; i<A.length; i++){
if(temp.includes(A[i])){
position = temp.indexOf(A[i]);
index = A.indexOf(A[i]);
delete temp[position];
delete A[index];
delete A[i];
}else{
temp[i] = A[i];
}
}
for(let j=0; j<A.length; j++){
if(A[j] !== undefined) return A[j];
else continue;
}
}
To test it, source data can look like [2,3,6,7,3,5,5,6,2] and it will give an output of 7. But when the array is so large up to [1,2,....] with length n = n=999,999, or n = 5000,000,000, the time complexity increases exponentially.

You might use Object to store non-paired elements only.
Please note that you don't need to store all the array elements and their counts in the Object and then filter by count (like #StepUp does).
Everything's been done in a single loop.
The function returns Array of all non-paired elements:
const solution = A => Array.isArray(A) ?
Object.keys(
A.reduce((r, k) => {
r[k] = r[k] || 0;
if (++r[k] > 1) delete r[k];
return r;
}, {})
) : [];
console.log(solution([2, 3, 6, 7, 3, 5, 5, 6, 2]))

We can try to find odd occurrences for one iteration by using great features of object. Object is key - value pair. So access to object key is O(1). So when we meet the same element, then we just increment value:
const hashMap = arr.reduce((a, c)=> {
a[c] = a[c] || 0;
a[c] += 1;
return a;
},{})
const result = Object.keys(hashMap).filter(key => hashMap[key] === 1);
An example:
let arr = [2, 3, 6, 7, 3, 5, 5, 6, 2];
const hashMap = arr.reduce((a, c)=> {
a[c] = a[c] || 0;
a[c] += 1;
return a;
},{})
const result = Object.keys(hashMap).filter(key => hashMap[key] === 1);
console.log(result);

My two 100% JavaScript solutions with optimized time complexity. The first one is using Set:
function solution(A) {
const pairs = new Set();
for (const num of A) {
if (pairs.has(num)) {
pairs.delete(num);
} else {
pairs.add(num);
}
}
const [unpaired] = pairs;
return unpaired;
}
The second one is using bitwise XOR:
function solution(A) {
let unpaired;
for (const num of A) {
unpaired ^= num;
}
return unpaired;
}

Related

javascript function to find the second largest element in an array

I am completing the hackerrank's 10 days of javascript challenge. The question:
write a function to take an array as an argument and then return the second largest element in the array.
I have written the code but my code is returning the largest element and not the second largest as asked.
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large=nums[0];
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&large<largest)
large=nums[j];
}
return large;
}
When input array nums={2,3,6,6,5} the result is coming 6 while expected output is 5. Please help and point out the errors in the function code below.
should not initialize large with first value var large=nums[0]; because it may appear the biggest value and won't work
should use nums[j]<largest instead of large<largest as mentioned above
I think don't need second loop as all checks can be done in first loop, and you can assign prev largest to large whenever you change it:
function getSecondLargest(nums) {
var largest = nums[0];
var large;
for (let i = 1; i < nums.length; ++i) {
if (nums[i] > largest) {
large = largest;
largest = nums[i];
} else if (nums[i] > large || typeof large === 'undefined') {
large = nums[i]
}
}
return large;
}
console.log(getSecondLargest([5,1-2,3]))
console.log(getSecondLargest([-5,1,-2,3]))
GET SECOND LARGEST
first, I create new array with unique values.
let arr = [...new Set(nums)];
second, sort value using built-in function .sort().
note : by default .sort() always sorts asciibetically, but for some testcase, it doesn't work. So, I put (a, b) => { return a - b } to make sure it will work properly.
arr = arr.sort((a, b) => { return a -b });
third, get the value from arr
let result = arr[arr.length - 2] || arr[0];
finally, return the result
return result
function getSecondLargest(nums) {
let arr = [...new Set(nums)];
//Javascript's array member method .sort( always sorts asciibetically.
arr = arr.sort((a, b) => { return a - b });
let result = arr[arr.length - 2] || arr[0];
return result
}
Just one minor change:
Use nums[j]<largest instead of large<largest in the second for loop
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large;
//To ensure that the selected number is not the largest
for(let j=0;j<nums.length;++j)
{
if (nums[j] !== largest){
large = nums[j];
break;
}
}
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&nums[j]!=largest)
large=nums[j];
else
console.log(large)
}
return large;
}
var secondLargest = getSecondLargest([6,3,6,6,5]);
console.log("Second largest number", secondLargest);
If you want to avoid using library functions like #ifaruki suggests, this line
if(large<nums[j]&&large<largest)
should read
if (large<nums[j] && nums[j] < largest)
Sorting and picking the second or second-to-last value fails when there are duplicates of the highest value in the input array.
Another easiest logic is to remove duplicates from the array and sort.
let givenArray = [2, 3, 6, 6, 5];
let uniqueArray = [...new Set(givenArray)];
console.log("The second largets element is", uniqueArray.sort()[uniqueArray.length - 2]);
I know you had your question answered, just thought I would provide my solution for any future users looking into this.
You can use reduce to go through the array while remembering the two largest numbers so far.
You just make a simple reduction function:
function twoMax(two_max, candidate)
{
if (candidate > two_max[0]) return [candidate,two_max[0]];
else if (candidate > two_max[1]) return [two_max[0],candidate];
else return two_max;
}
And then you use it for example like this:
let my_array = [0,1,5,7,0,8,12];
let two_largest = my_array.reduce(twoMax,[-Infinity,-Infinity]);
let second_largest = two_largest[1];
This solution doesn't require sorting and goes through the array only once.
If you want to avoid using **sort method. I think here's the easiest logic to do that, which will also work in arrays where there's duplicates of largest integer exists.
function getSecondLargest(arr) {
const largest = Math.max.apply(null, arr);
for (let i = 0; i < arr.length; i++) {
if (largest === arr[i]) {
arr[i] = -Infinity;
}
}
return Math.max.apply(null, arr);
}
console.log(getSecondLargest([5, 7, 11, 11, 11])); //7

Efficient way to dedupe large list of arrays

I have a very large array of arrays (on the order of 960,799 entries or possibly much larger). I need to process it into a new array such that:
Each sub-array contains no duplicates.
The main array contains no duplicate sub-arrays.
The problem is that "duplicate sub-arrays" must include arrays with the same values in a different order. In other words, if I had these sub-arrays:
[[1,2,3], [1,2,3], [3,1,2]]
They would all be considered duplicates and only one would be kept (any of them, it doesn't matter; I've been just keeping the first one; it's also fine if the order of the selected sub-array doesn't actually match, i.e. if the order of elements in the sub-array changes during processing).
My attempted solution has been to map all the sub-arrays into strings based on de-duping the sub-array, sorting it, and joining it with a delimiter. Then I de-dupe that final array, then map them back to arrays with a split. It works, but the process is extremely slow. It takes over 30 seconds for a single pass, and since the array I end up processing can grow exponentially larger, this is not acceptable. I need a more efficient algorithm.
Here's the code I'm using now that's slow (ret is the input array):
const stringList = ret.map(list => {
return [...new Set(list)].sort().join('|');
});
const hashSet = new Set(stringList);
const output = [...hashSet].map(str => str.split('|'));
Can anyone help me get the same result more efficiently? Thanks.
EDIT
To elaborate, I'm getting these massive input arrays by calculating what is essentially the power set of some input of strings. This is the code; if it's possible to stop it from producing duplicate entries in the first place, that would work well, too, I think:
// Calculate the Cartesian product of set s
function cart(s) {
return s.reduce((acc, val) => {
return acc.map((x, i) => {
return val.map(y => {
return x.concat([y]);
});
}).flat();
}, [[]]);
}
// Use the Cartesian product to calculate the power set of set s
function pset(s) {
let ret = [];
for (let i = 0; i < s.length; ++i) {
const temp = [];
for (let j = 0; j <= i; ++j) {
temp.push([].concat(s));
}
ret = ret.concat(cart(temp));
}
return ret;
}
You could generate the power set without duplicates.
function pset(array) {
function iter(index, temp) {
if (index >= array.length) {
temp.length && result.push(temp);
return;
}
iter(index + 1, temp.concat(array[index]));
iter(index + 1, temp);
}
var result = [];
iter(0, []);
return result;
}
console.log(pset(['a', 'b', 'c']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Given that I'm not able to perform a benchmark with real data, I can't verify how much faster this approach is for your use case, but by using basic for loops and avoiding functional code as much as conveniently possible, I've come up with the following:
const ret = [[1, 2, 3], [1, 2, 3], [3, 1, 2], [1, 4, 5], [4, 1, 5]];
function ascending (a, b) {
// works for strings and numbers
return -(a < b) || +(a > b);
}
function ascending2d (a, b) {
const aLength = a.length;
const bLength = b.length;
const length = Math.min(aLength, bLength);
for (let i = 0; i < length; ++i) {
const difference = ascending(a[i], b[i]);
if (difference !== 0) return difference;
}
return aLength - bLength;
}
for (let i = 0; i < ret.length; ++i) {
ret[i].sort(ascending);
}
ret.sort(ascending2d);
const output = [ret[0]];
for (let i = 1; i < ret.length; ++i) {
const value = ret[i];
if (ascending2d(ret[i - 1], value) !== 0) output.push(value);
}
console.log(output);
Let me know if this is an improvement over your current approach. You can always improve performance further by profiling your code and looking for bottlenecks that can be re-written.
Performance Benchmark
I've published a benchmark using the test data in my example here, comparing your original solution, my solution, and Andrew's solution. I couldn't include Nina's for comparison because hers doesn't perform deduplication on ret, instead it modifies the generation of ret.
EDIT: Nevermind, my implementation had no benchmarks. It is slower. Due to the underlying implementation of JSON.parse, JSON.stringify, and the default algorithm for Array#sort.
Since you're looking for bleeding edge performance, it's hard to get an elegant solution. If you instantiate an object with Object.create(null) you minimize the overhead for O(1) insertion. It creates a POJO with no prototype. You also don't need to check in the for in loop for Object.hasOwnProperty, because there's no prototype to search.
const ret = [[], [1, 2, 3], [3, 1, 2], [1, 4, 5], [4, 1, 5]];
const hashMap = Object.create(null)
function createUniqArraysOfPrimitiveArrays(ret) {
for (let i = 0; i < ret.length; i++) {
const currEl = ret[i]
if (currEl.length === 0) {
hashMap['[]'] = null
} else if (currEl.length === 1) {
hashMap[`[${currEl[0]}]`] = null
} else {
hashMap[JSON.stringify(currEl.sort())] = null
}
}
const outputArray = []
for (const array in hashMap) {
outputArray.push(JSON.parse(array))
}
return outputArray
}
console.log(createUniqArraysOfPrimitiveArrays(ret))

how to print a unique number in a array

The problem is to find the unique number in a array such as [2,2,2,5].
The output should be 5 as it is the 1 unique element in the array.
I have attempted this:
function findUniq(arr) {
var b= arr[0];
var c;
for(var i=0; i<arr.length; i++)
{
if(arr[i]===b )
{
b=arr[i]
}
else
{
c=arr[i];
}
}
return c
console.log(findUniq([3, 5, 3, 3, 3]))
This works fine unless the unique number is the first element in the array. How do I fix this?
You can use indexOf and lastIndexOf to see if a value occurs more than once in the array (if it does, they will be different), and if so, it is not the unique value. Use filter to process the array:
let array = [2,2,2,5];
console.log(array.filter(v => array.indexOf(v) === array.lastIndexOf(v)));
array = [5,3,3,3,3];
console.log(array.filter(v => array.indexOf(v) === array.lastIndexOf(v)));
array = [4,4,5,4];
console.log(array.filter(v => array.indexOf(v) === array.lastIndexOf(v)));
You can create a recursive function that will take the first element of the array and see if it exists in the rest of it, if it does, it will take the next element and do the same, return the element if it doesn't exist in the rest of the array :
const arr = [3, 3, 3, 5, 3];
const find = arr => {
const [f, ...rest] = arr;
if(rest.includes(f))
return find(rest);
else
return f;
}
const result = find(arr);
console.log(result);
Note that this will return the last element if all of them are the same [3,3,3] will return 3
Try something like this using a set, which only stores unique elements:
var set = new Set(arr);
// count instances of each element in set
result = {};
for(var i = 0; i < a.length; ++i) {
if(!result[arr[i]])
result[arr[i]] = 0;
++result[arr[i]];
}
for (var value in result) {
if (value == 1) {
return value;
}
}
// if there isn't any
return false;
This should work, please tell me if it doesn't.
This is another implementation that is surely less efficient than that of #Nick's, but it is a valid algorithm anyway:
function findUniq(arr) {
var elemCount = new Map();
var uniq = [];
// Initialize elements conts
for (var k of arr.values()) {
elemCount.set(k, 0);
}
// Count elements
for (var k of arr.values()) {
elemCount.set(k, elemCount.get(k) + 1);
}
// Add uniq elements to array
for (var [k, v] of elemCount.entries()) {
if (v === 1) uniq.push(k);
}
return uniq;
}
console.log(findUniq([3, 5, 3, 3, 3]))
if you prefer .reduce over .map for your use case (for performance/etc. reasons):
function existance(data) {
return data.reduce((a, c) => (data.indexOf(c) === data.lastIndexOf(c)) ? a.concat(c) : a, []);
}
console.log(existance([1,1,1,2]));
console.log(existance([1,1,2,3,4,5,5,6,6,6]));

refactorized solve for an algorithm in javascript

I have attended a technical interview for a development company. They asked me the following:
Giving an array of numbers (n) find 2 numbers that sum gives (k) and print them.
e.g
Input: n = [2,6,4,5,7,1], k = 8
Output: result=(2,6),(7,1)
My solution:
function findSum(n,k){
let aux = []
for (let i = 0; i < n.length; i++) {
for (let j = i+1; j < n.length; j++) {
if (n[i] + n[j] == k) {
aux.push({ first: n[i], second: n[j] })
}
}
}
return aux;
}
They told me that, it is possible to make the exercise with some kind of key or mapping.
Does some one know how to do it with only one loop?
The key to solving a question like this with low time complexity is the ability to efficiently search the data structure. A lot of answers rearrange the array in a way where searching an array is optimized. Another approach is with a data structure that inherently has fast search.
Set and Map data structures have O(1) time complexity for searches, which make them good data structures where searching can be leveraged to increase performance.
I use a new Map and traverse the array while adding it as a key. I set the key to the number and the value to the number of times I see it. I use a map over a new Set because I can also keep track of the number of instances of that particular number.
I search for the number that would sum up to k, which is: (k - num). If I find that number, I add both numbers to my results data structure and decrement the value by 1, to show that it's been used.
Time complexity: O(n), memory complexity: O(2n). Twice the amount of space compared to the original array because I have a key and a value to store in my Map
function pairSums(arr, k){
const map = new Map
const matches = []
for (let num of arr) {
const search = k - num
if (map.get(search) > 0) {
matches.push([num, k - num])
map.set(search, map.get(search) - 1)
} else if (!map.has(num)){
map.set(num, 1)
} else {
map.set(num, map.get(num) + 1)
}
}
return matches
}
console.log(pairSums([2, 6, 6, 6, 2, 4, 4, 4, 5, 7, 1, 4, 2], 8))
Match a number x from array with a key Math.min(x, k - x). Then run through your array and store every number in a hash using mentioned key. When the key you are going to add already is in the hash - check if stored value and current number gives required sum.
function findSum(n, k){
let hash = {};
for(let i = 0; i < n.length; ++i){
let x = n[i], key = Math.min(x, k - x);
if((key in hash) && hash[key] + x == k)
return [hash[key], x];
else hash[key] = x;
}
}
A task like this can be as simple or as complicated as you want to make it. Here's one solution, for example:
function findPairs(n, k) {
return n.reduce((pairs, next, i) => pairs.concat(
n.slice(i + 1)
.filter(num => next + num === k)
.map(num => [ next, num ])
),
[]
);
}
For the inputs [2, 6, 4, 5, 7, 1] and 8 will output [ [2, 6], [7, 1] ].
From https://www.geeksforgeeks.org/write-a-c-program-that-given-a-set-a-of-n-numbers-and-another-number-x-determines-whether-or-not-there-exist-two-elements-in-s-whose-sum-is-exactly-x/:
Sort the array in non-decreasing order.
Initialize two index variables to find the candidate elements in the sorted array. Initialize l to the leftmost index: l = 0, Initialize r to the rightmost index: r = n.length - 1
Loop while l < r.
if (n[l] + n[r] == k) then return 1
else if( n[l] + n[r] < k ) then l++
else r--
No candidates in whole array - return 0
I think by sorting you can do that
var n = [2,6,4,5,7,1];
var k = 8 ;
n.sort();
let start = 0, end = n.length-1;
while(start < n.length && end >= 0) {
let current_sum = (n[start] + n[end]);
if(current_sum == k) {
console.log('Found sum with '+ n[start] +' and '+ n[end]);
break;
}
else if(current_sum > k) {
end--;
} else {
start++;
}
}
if(start == n.length || end < 0) {
console.log('Not Found');
}
but while writing this code I got one another approach also
const set = new Set([2,6,4,5,7,1]);
var k = 8;
let found = false;
for (let item of set) {
let another = k - item;
if(set.has(another)){
console.log('found with '+item +' and ' +another);
found = true;
break;
}
}
if(!found) {
console.log('Not found');
}
If numbers are non-negative and the target value is within JavaScript array limit:
function findsums(arr,k){
var ret=[];
var aux=[];
arr.forEach(function(i){
if(i<=k){
if(aux[k-i])
ret.push([k-i,i]);
aux[i]=true;
}
});
return ret;
}
console.log(findsums([2,6,4,5,7,1],8));
Similar approach could work with a bitfield (or even with a sparse array of bitfields) too.
Minified alternative similar to #Andrew's great answer, but assumes that all numbers are above 0 :
var pairs = (arr, k) => arr.reduce((a, n) =>
(a[n - k]-- ? a.push([n, k - n]) : a[-n] = a[-n] | 0 + 1, a), []);
console.log(JSON.stringify( pairs([2,6,4,5,7,1], 8) ));

How do I find all 2 pairs of integers that have the same product in JavaScript?

I need to write a program that, when given a list of integers, it finds all 2-pairs of integers that have the same product. i.e. a 2-pair is 2 distinct pairs of integers lets say [(a,b),(c,d)] where a*b = c*d but a ≠ b ≠ c ≠ d.
The range of integers should be from 1 to 1024. What I would like to implement is that when the web page is opened the user is prompted by a pop up in which he will enter the array of integers, i.e [1,2,3,7,8,9,6] etc for instance from the input [1,2,3,7,8,9,6] the output should be [(9,2),(3,6)] since both evaluate to 18.
The coding I did so far is very basic and can be seen below. What I've done so far is the pop-up box alert the input etc, but can't seem to understand how to make the program check for the pairs and give the sum. Thanks in advance to this community who's helping me out to better understand and learn javascript!
I've done my fair bit of research below, definitely different question than mine but have gone through them.
Find a pair of elements from an array whose sum equals a given number
https://www.w3resource.com/javascript-exercises/javascript-array-exercise-26.php
Code:
function evaluate() {
const input = prompt("Please enter the array of integers in the form: 1,2,3,1")
.split(',')
.map(item => item.trim());
function pairs(items) {
}
if (input == "" || input == null) {
document.writeln("Sorry, there is nothing that can be calculated.");
} else {
document.writeln("Your calculation is: ");
document.writeln(pairs(input) + " with a starting input string of: " + input);
}
}
evaluate()
You could iterate the array and a copy of the array beginning by the actual index plus one for getting the products. Store the result in an object with product as key.
Then get the keys (products) of the object, filter it to get only the results with two or more products.
var array = [1, 2, 3, 7, 8, 9, 6],
result = {},
pairs;
array.forEach(function (a, i) {
array.slice(i + 1).forEach(function (b) {
(result[a * b] = (result[a * b] || [])).push([a, b]);
});
});
pairs = Object
.keys(result)
.filter(function (k) { return result[k].length >= 2; })
.map(function(k) { return result[k]; });
console.log(pairs);
We could mutate the equation:
a * b = c * d | : b
a = c * d : b
So actually we just need to get all different combinations of three numbers (b, c, d) and check if the result (a) is also in the given range:
while(true){
// shuffle
const [b, c, d] = items;
const a = c * d / b;
if(items.includes(a + ""))
return true;
}
return false;
Now you only need to shuffle the array to go through all different combinations. You can find an algorithm here
Assuming that you are given an array such as [1,2,3,7,8,9,6] and a value 18 and you need to find pairs that multiply to 18 then, use the following approach
Convert them to a map - O(n)
var inputArr = [1,2,3,7,8,9,6];
var map = inputArr.reduce( (acc, c) => {
acc[ c ] = true; //set any truthy value
return acc;
},{});
Iterate an inputArr and see if its compliment is available in the map - O(n)
var output = [];
var mulValue = 18;
inputArr.forEach( s => {
var remainder = mulValue/s;
if ( map[s] && map[remainder] )
{
output.push( [ s, remainder ] );
map[s] = false;
map[remainder] = false;
}
});
Demo
var inputArr = [1, 2, 3, 7, 8, 9, 6];
var map = inputArr.reduce((acc, c) => {
acc[c] = true; //set any truthy value
return acc;
}, {});
var output = [];
var mulValue = 18;
inputArr.forEach(s => {
var remainder = mulValue / s;
if (map[s] && map[remainder]) {
output.push([s, remainder]);
map[s] = false;
map[remainder] = false;
}
});
console.log(output);
You can try something like this:
Idea:
Loop over the array to compute product. Use this iterator(say i) as get first operand(say op1).
Now again loop over same array but the range will start from i+1. This is to reduce number of iteration.
Now create a temp variable that will hold product and operand.
On every iteration, add value to product in hashMap.
Now loop over hashMap and remove any value that has length that is less than 2.
function sameProductValues(arr) {
var hashMap = {};
for (var i = 0; i < arr.length - 1; i++) {
for (var j = i + 1; j < arr.length; j++) {
var product = arr[i] * arr[j];
hashMap[product] = hashMap[product] || [];
hashMap[product].push([arr[i], arr[j]]);
}
}
for(var key in hashMap) {
if( hashMap[key].length < 2 ) {
delete hashMap[key];
}
}
console.log(hashMap)
}
sameProductValues([1, 2, 3, 7, 8, 9, 6])

Categories