How to map two arrays based on comparative value - javascript

I have two arrays:
A sequential array of numbers between 1-8, in increments of 1:
a = [1,2,3,4,5,6,7,8]
A sequential array of numbers between 1-8, with random increments:
b = [1,2,5,7]
I want to create a list of {a:val, b:val} dict pairs, where the value of b is only the next in the array if it is equal or greater to the value of a:
c = [
{a:1, b:1},
{a:2, b:2},
{a:3, b:2},
{a:4, b:2},
{a:5, b:5},
{a:6, b:5},
{a:7, b:7},
{a:8, b:7}
]
Is there an easy way to do this? I thought about using compounding $.each loops to build new arrays and increment values, but it seems as though this is overkill?

You can use map and shift
Loop over first array.
If the value of current element is greater then first element on second array remove first element from second array, ( except when the length is less then 2)
return object in desired format
let a = [1, 2, 3, 4, 5, 6, 7, 8]
let b = [1, 2, 5, 7]
let final = a.map(v => {
if (v >= b[1] && b.length > 1) {
b.shift()
}
return {
a: v,
b: b[0]
}
})
console.log(final)
This mutates second array if you don't want to mutate you can use a index variable and increment it based on condition
let a = [1, 2, 3, 4, 5, 6, 7, 8]
let b = [1, 2, 5, 7]
let index = 0
let final = a.map(v => {
if (v >= b[index+1] && b.length - 1 > index ) {
index++
}
return {
a: v,
b: b[index]
}
})
console.log(final)

The first array is actually redundant - the only significant information it carries is the number of elements.
The following code determines the difference between 2 adjacent threshold values from array b. This is 1 less the number of array elements with object property b set to the lower of these threshold values.
The target array is constructed as the array of object properties b in proper order. The resulting value list is mapped to the desired format.
The code may be optimized. It should run fast if the length of array b is much less than the length of array a. This hypothesis is not tested though and given the low complexity of the code/data structures will probably be insignificant.
let a = [1, 2, 3, 4, 5, 6, 7, 8]
, b = [1, 2, 5, 7]
, c = []
, n_last = 1
;
b.forEach ( (pn_current, pn_idx) => {
c.push( ...((new Array(pn_current - n_last)).fill(n_last)) );
n_last = pn_current;
});
c.push( ...((new Array(a.length + 1 - n_last)).fill(n_last)) );
c = c.map ( ( pn_entry, pn_idx ) => { return { a: pn_idx+1, b: pn_entry }; } );
console.log(c);

Related

How to iterate over an array multiple times without repeating summed elements

I am trying to solve this problem but I don't know why I can't pass all test cases. I need some help and explanation, how can I count some array (in this example: variable s) multiple times and not repeat the same elements that I already summed.
Problem description:
Lily has a chocolate bar that she wants to share it with Ron for his
birthday. Each of the squares has an integer on it. She decides to
share a contiguous segment of the bar selected such that the length of
the segment matches Ron's birth month and the sum of the integers on
the squares is equal to his birth day. You must determine how many
ways she can divide the chocolate.
Consider the chocolate bar as an array of squares, s=[2,2,1,3,2].
She wants to find segments summing to Ron's birth day, d=4 with a
length equalling his birth month, m=2. In this case, there are two
segments meeting her criteria: [2,2] and [1,3].
Function Description
Complete the birthday function in the editor below. It should return
an integer denoting the number of ways Lily can divide the chocolate
bar.
birthday has the following parameter(s):
s: an array of integers, the numbers on each of the squares of
chocolate,
d: an integer, Ron's birth day, m: an integer, Ron's birth month
My code:
function birthday(s, d, m) {
let bars = 0;
if (m !== 1) {
s.reduce((acc, val) => (acc+val) === d ? ++bars : bars)
} else {
bars = 1;
}
return bars;
}
Some cases:
s = [2, 5, 1, 3, 4, 4, 3, 5, 1, 1, 2, 1, 4, 1, 3, 3, 4, 2, 1]
d = 18
m = 7
s = [4, 5, 4, 5, 1, 2, 1, 4, 3, 2, 4, 4, 3, 5, 2, 2, 5, 4, 3, 2, 3,
5, 2, 1, 5, 2, 3, 1, 2, 3, 3, 1, 2, 5]
d = 18
m = 6
s = [4, 5, 4, 2, 4, 5, 2, 3, 2, 1, 1, 5, 4]
d = 15
m = 4
My code works with this:
s = [1, 2, 1, 3, 2]
d = 3
m = 2
This can be found on HackerRank > Practice > Algorithms > Implementation
You just have to slice the array with the sliced length of m, and then compare that to d
As slice doc:
The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end (end not included) where start and end represent the index of items in that array. The original array will not be modified.
For example:
s = [1, 2, 1, 3, 2]
m = 2
d = 3
// We loop through s with index stop at s.length - m + 1 for slice to be in correct range
// Slices:
i=0: [1, 2] -> sum=3 -> res=0+1=1
i=1: [2, 1] -> sum=3 -> res=1+1=2
i=2: [1, 3] -> sum=4 -> do nothing
i=4: [3, 2] -> sum=5 -> do nothing
Below is a worked solution
function birthday(s, d, m) {
let res = 0
const sum = (arr) => arr.reduce((acc, el) => acc + el, 0)
for (let i = 0; i < s.length - m + 1; i++) {
if (sum(s.slice(i, i + m)) === d) {
res++
}
}
return res
}
Whenever you are looping over an array to get the summation or do a mathematical equation on it and you have to remove that specific element that you already calculated, You can use one of these built in function to remove an element from an array using a specific index.
Array.prototype.slice()
&& Array.prototype.splice()
Here's an easy to understand way with nested loops:
function birthday(s, d, m) {
var matches = 0; // Total matches found
// Look at chunks starting a position 0. Last chunk can't be m spots past end of array, so last chunk starts at 1 + s.length - m:
for ( let i=0; i < 1 + s.length - m; i++ ) {
var sum = 0; // What this chunk sums to
// Sum up the values of this chunk:
for ( let j=0; j < m; j++ ) {
sum += s[i+j];
}
if ( sum === d ) { // Does this chunk sum to d?
matches++; // Yes!
}
}
return matches;
}

Given an array and positive integer d, left shift the array by d efficiently

This is the HackerRank problem description:
A left rotation operation on an array of size n shifts each of the array's elements d unit to the left. For example, if left rotations are performed on array [1,2,3,4,5], then the array would become [3,4,5,1,2].
Here is my function :
function leftRotation(arr, d) {
let newArr = [];
while (d > 0) {
let first = arr.shift();
newArr.push(first);
d--;
}
return [...arr,...newArr];
}
console.log(leftRotation([1,2,3,4,5], 2))
but it doesn't pass large test cases. For example, for n=73000 and d=60000.
Thanks in advance for any idea .
I'm not entirely sure the performance of this method, but it can be done in a single line.
function leftRotation(arr, d) {
return [ ...arr.slice(d), ...arr.slice(0, d) ];
}
console.log(leftRotation([1, 2, 3, 4, 5], 2));
Don't rotate N number of times. Shift N % (length of array) times, because Let's say you have an array of 5 items and you are asked to shift it 5 times then you essentially do not have to shift even once.
Start : [1, 2, 3, 4, 5]
1: [2, 3, 4, 5, 1]
2: [3, 4, 5, 1, 2]
3: [4, 5, 1, 2, 3]
4: [5, 1, 2, 3, 4]
5: [1, 2, 3, 4, 5]
EDIT:
You could use similar logic to optimize the code instead of actually shifting elements in the array. For example: In case of N = 73000, D = 60000, you could splice the array by 73000 % 60000 and then just append the returned spliced array to the existing array and return it.
For an array arr a method shift() can have a time complexity of O(arr.length).
If d is larger than n then you still perform shift() d times.
In total, your time complexity can rise to O(d * arr.length) that is definitely too long.
However, this problem can be solved in O(arr.length) time and space. If you know d then you can shift each item by d positions to the left easily. For instance, arr.length = 5 and d = 2
position: 0 1 2 3 4
arr: 4 3 5 1 6
position in arr: 2 3 4 0 1
shifted_arr: 5 1 6 4 3
So actually, each item at position i in the shifted array corresponds to an item at position (i + d) % arr.length in the original array arr. Hence, the code can look as follows:
function leftShift(arr, d) {
let newArr = [];
let size = arr.length;
for (var i = 0; i < size; i++) {
newArr.push(arr[(i + d) % size]);
}
return newArr;
}
console.log(leftShift([4, 3, 5, 1, 6], 2))

Check in an Array if I have three same values, without knowing the value [duplicate]

This question already has answers here:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
(97 answers)
Closed 4 years ago.
I have an array with random numbers, from 1 to 6.
How can I know if I have in this array two, three or four same numbers ? And what is that number ? Or if I have two numbers that they appear twice ?
array = [1, 3, 5, 5, 6];
x = array[0];
repetitions = 0;
for (let i = 0; i < 5; i++) {
const y = array[i];
if (x === y) {
repetitions++;
}
}
Should I do something like this for each element of array ?
Thank you!
This is a simple solution that can be provided using filter;
var array = [1,2, 3,4,4,4,4,4,4,4, 5, 5, 6,6,6];
var duplicate = [];
var newArray = array.filter((v, i) =>{
if(array.indexOf(v) == i){
return v
}else{
duplicate.indexOf(v) == -1 ? duplicate.push(v):'';
}
})
console.log(`new array ${newArray}`);
console.log(`Duplicate values array ${duplicate}`);
There are some constraints you should clarify. For example, this code would returns the first number in the array that is duplicated:
let duplicated = [...array] // makes a copy of the array
.sort() // since `sort` mutate the original
.find((item, index, arr) => value === arr[index + 1]);
console.log(duplicated) // 5, in your case
If there is no number duplicated, then it will return undefined.
However, maybe that is not what you want. Considering this array: [1, 3, 6, 5, 5, 6, 6, 6]. Even if there is also 6 duplicated, you will always return 5.
If you want just to know if there is at least a number duplicated, the code above will works. If you want to know all the duplicated number, and in which order they appear first in the original array, you need something different. For example:
let array = [1, 3, 6, 5, 5, 6, 6, 6];
let occurrences = array
.reduce((acc, value) => (acc[value]=-~acc[value], acc),{});
console.log(occurrences); // {"1": 1, "3": 1, "5": 2, "6": 4}
At this point you can decided what do you want to do with this data, e.g. you can filter out the numbers that appears in the array just once:
console.log(
Object.fromEntries(
Object.entries(occurrences).filter(([num, count]) => count > 1)
)
); // {"5": 2, "6": 4}
etc.
UPDATE (see comments):
Object.fromEntries is a method recently introduced, in case having a filtered object with all the occurrences is the goal, in this context could be easily replaced by a function like this:
const fromEntries = entries =>
entries.reduce((acc, [key, value]) => (acc[key] = value, acc), {});
You check if the value in array exists more than 2 times by compare indexOf and lastIndexOf the value
function duplicates(arr){
let result = []
arr.forEach(item => {
if(arr.indexOf(item) !== arr.lastIndexOf(item)){
result.push(item)
}
})
return [... new Set(result)];
}
console.log(duplicates([1,2,3,5,5,6,6,6]));
You could take a Map and count all occurences of the values.
The result is an array where the first element is the value and the second is the count of the value.
var array = [1, 3, 5, 5, 6],
counts = Array.from(array.reduce((m, v) => m.set(v, (m.get(v) || 0) + 1), new Map));
console.log(counts);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to subtract one array from another, element-wise, in javascript

If i have an array A = [1, 4, 3, 2] and B = [0, 2, 1, 2] I want to return a new array (A - B) with values [1, 2, 2, 0]. What is the most efficient approach to do this in javascript?
const A = [1, 4, 3, 2]
const B = [0, 2, 1, 2]
console.log(A.filter(n => !B.includes(n)))
Use map method
The map method takes three parameters in it's callback function like below
currentValue, index, array
var a = [1, 4, 3, 2],
b = [0, 2, 1, 2]
var x = a.map(function(item, index) {
// In this case item correspond to currentValue of array a,
// using index to get value from array b
return item - b[index];
})
console.log(x);
For Simple and efficient ever.
Check here : JsPref - For Vs Map Vs forEach
var a = [1, 4, 3, 2],
b = [0, 2, 1, 2],
x = [];
for(var i = 0;i<=b.length-1;i++)
x.push(a[i] - b[i]);
console.log(x);
const A = [1, 4, 3, 2]
const B = [0, 2, 1, 2]
const C = A.map((valueA, indexInA) => valueA - B[indexInA])
console.log(C) // [1, 2, 2, 0]
Here the map is returning the substraction operation for each number of the first array.
Note: this will not work if the arrays have different lengths
If you want to override values in the first table you can simply use forEach method for arrays forEach. ForEach method takes the same parameter as map method (element, index, array). It's similar with the previous answer with map keyword but here we are not returning the value but assign value by own.
var a = [1, 4, 3, 2],
b = [0, 2, 1, 2]
a.forEach(function(item, index, arr) {
// item - current value in the loop
// index - index for this value in the array
// arr - reference to analyzed array
arr[index] = item - b[index];
})
//in this case we override values in first array
console.log(a);
One-liner using ES6 for the array's of equal size in length:
let subResult = a.map((v, i) => v - b[i]); // [1, 2, 2, 0]
v = value, i = index
function subtract(operand1 = [], operand2 = []) {
console.log('array1', operand1, 'array2', operand2);
const obj1 = {};
if (operand1.length === operand2.length) {
return operand1.map(($op, i) => {
return $op - operand2[i];
})
}
throw new Error('collections are of different lengths');
}
// Test by generating a random array
function getRandomArray(total){
const pool = []
for (let i = 0; i < total; i++) {
pool.push(Math.floor(Math.random() * total));
}
return pool;
}
console.log(subtract(getRandomArray(10), getRandomArray(10)))
Time Complexity is O(n)
You can also compare your answer with a big collection of arrays.

What's the most concise way in javascript to find consecutive dups in an array, sum them up and reiterate?

Example
I have an array of:
const myArr = [1, 1, 1, 3, 2, 2, 4, 3];
If I were pass it to the function it should produce the output like so:
//result: [2, 1, 3, 8, 3]
The consecutive first two 1s and 2s were consecutive duplicates so they were summed up.
The first two consecutive 1s became 2 so it now doesn't match.
The consecutive 2s became 4 so it matched the next running number and summed up again resulting in an 8
Although the 3s are duplicate they we're not summed up because they're not consecutive
Bonus
It would be great if someone can demonstrate this using javascript's array functions map or reduce.
Is this possible without any kind of recursion? someone mentioned its possible, then I'd be nice if it didn't use recursion
EDIT
Adding this here because some have already provided answers.
In the case of:
const myArr = [1, 1, 1, 1];
// result: [4]
const myArr = [1, 1, 1, 1, 3, 2, 2, 4, 3];
// result: [4, 3, 8, 3]
Another interesting case that didn't pass my mind:
const myArr = [1, 1, 2, 2];
//result [4, 2] instead of [2,4]
It should check against the new array as soon a change was made
With the assumption that this will be used only on arrays of numbers, this is simple enough to do with reduce.
function duplicateAddition(input) {
var last;
var result = input;
do {
last = result;
result = last.reduce(function (carry, item) {
if (carry[carry.length - 1] == item) {
carry[carry.length - 1] *= 2;
} else {
carry.push(item);
}
return carry;
}, []);
} while (last.length != result.length);
return result;
}
Assuming the array always contains numbers, the most concise I can think of would be
function merge(arr) {
var x = String(arr),
y = x.replace(/([^,]),\1/, (_,m)=>2*m);
return x.length == y.length ? y.split(",").map(Number) : merge(y);
}
If you want to avoid the tail recursion, it's trivial to convert that to a loop:
function merge(arr) {
var y = String(arr);
do {
var x = y;
y = x.replace(/([^,]),\1/, (_,m)=>2*m);
} while(x.length != y.length)
return y.split(",").map(Number);
}
or
function merge(arr) {
for (var x=String(arr), y=""; x.length!=y.length; x=y.replace(/([^,]),\1/, (_,m)=>2*m))
y=x;
return x.split(",").map(Number);
}
Btw, to alter the behaviour of the algorithm to get [2, 4] instead of [4, 2] from [1, 1, 2, 2] (i.e. find all duplicates and sum them before reiterating), you'd only need to add the /global flag to the regex.
This solution uses a recursive generator. It has the advantage that it pretty much says what it means and does what it says.
function* x([first, second, ...rest]) {
if (first !== undefined) {
if (first !== second) yield first; else second *= 2;
yield* x([second, ...x(rest)]);
}
}
function test(a) { console.log(Array.from(x(a))); }
test([1, 1, 1, 3, 2, 2, 4, 3]);
test([1, 1, 1, 1]);
test([1, 1, 2, 2]);
In this solution, the input [1, 1, 2, 2] produces [2, 4]. That is because the logic greedily collapses the 2, 2 into a 4, which then does not match the 2 resulting from 1, 1. If this behavior is not desired, then it might suffice to change the line above to:
yield* x([second, ...rest]);
This is a simple game of unshifting from array a to pushing array b. So if a[0] === a[1] you take the first two items from array a, get their sum and push it into array b but if a[0] !== a[1] then take only the first item and push it into array b. However before we push the interim value (t in below snippet) we check the last item of array b to see if it's equal to the interim. If they are equal we just double the last item of array b if not just push interim (t) over b.
Then make a tail call optimized recursive invoking.
function sumAndReduce(a, b = []){
var t = 0;
if (!a.length) return b;
t = a[0] === a[1] ? a.splice(0,2).reduce((p,c) => p+c)
: a.splice(0,1)[0];
b[b.length-1] === t ? b[b.length-1] += t
: b.push(t);
return sumAndReduce(a,b);
}
var myArr = [1, 1, 1, 3, 2, 2, 4, 3],
result = sumAndReduce(myArr);
console.log(JSON.stringify(result));
console.log(JSON.stringify(sumAndReduce([1, 1, 1, 1])));
console.log(JSON.stringify(sumAndReduce([1, 1, 2, 2])));
Well... if you don't want recursion even if it is a tail call optimized one, then it's very trivial to convert the above code into an iterative one with a while loop. I'll leave it to you..!
So i can not make sure if your request as per an input [1,1,2,2] breaks the initial conditions but here is a modified version of the above code to comply with that as well.
function sumAndReduce(a, b = []){
var t = 0;
if (!a.length) return b;
t = a[0] !== a[1] || b[b.length-1] === a[0] ? a.splice(0,1)[0]
: a.splice(0,2).reduce((p,c) => p+c);
b[b.length-1] === t ? b[b.length-1] += t
: b.push(t);
return sumAndReduce(a,b);
}
var myArr = [1, 1, 1, 3, 2, 2, 4, 3],
result = sumAndReduce(myArr);
console.log(JSON.stringify(result));
console.log(JSON.stringify(sumAndReduce([1, 1, 1, 1])));
console.log(JSON.stringify(sumAndReduce([1, 1, 2, 2])));

Categories