How to increase count base on two array difference? - javascript

I am trying to get a count from an initial value based on an old value to a new value change.
Below example countA is the count of item 'a' in the array and countB is for b.
I have an initial start of the count of countA and countB. Now lets say I had an old array which is const old1 = ['a']; which is now updated to const new1 = ['a', 'b'];, in this case it should increase the countA to 5. Again if I had const old2 = ['a']; const new2 = ['b']; this would change my countA to 3 and countB to 6;
I wrote something that works, but I was hoping with a better algorithm. Is there a better way to do this?
let countA = 4; let countB = 5;
function arrayDiff(a, b) {
return [
...a.filter(x => !b.includes(x)),
...b.filter(x => !a.includes(x))
];
}
function checkAndUpdateCount(oldVal, newBVal) {
console.log('checkAndUpdateCount called ==========>');
const difference = arrayDiff(oldVal, newBVal);
console.log(difference);
difference.forEach(item => {
if (oldVal.includes(item) && newBVal.includes(item)) {
console.log('no Changes');
} else if (!oldVal.includes(item) && newBVal.includes(item)) {
console.log(countA, countB);
if (item === 'a') {
countA++;
} else {
countB++;
}
console.log(countA, countB);
} else if (oldVal.includes(item) && !newBVal.includes(item)) {
console.log(countA, countB);
if (item === 'a') {
countA--;
} else {
countB--;
}
console.log(countA, countB);
}
});
}
const old1 = ['a']; const new1 = ['a', 'b'];
const old2 = ['a']; const new2 = ['b'];
const old3 = ['a', 'b']; const new3 = ['b'];
const old4 = ['b', 'a']; const new4 = ['a'];
const old5 = ['b', 'a']; const new5 = [];
const old6 = []; const new6 = ['b', 'a'];
checkAndUpdateCount(old1, new1); // this will return 4 6
countA = 4; countB = 5;
checkAndUpdateCount(old2, new2); // 4 6
countA = 4; countB = 5;
checkAndUpdateCount(old3, new3); // 3 6
countA = 4; countB = 5;
checkAndUpdateCount(old4, new4); // 4 4
countA = 4; countB = 5;
checkAndUpdateCount(old5, new5); // 3 4
countA = 4; countB = 5;
checkAndUpdateCount(old6, new6); // 5 6
https://jsfiddle.net/t3rvzn7f/1/

You could take an object for counting elements and iterate with a closure over the wanted value for updating the count.
This approach uses only two loops, one for every array.
function updateCount(oldA, newA) {
const add = v => k => count[k] += v;
oldA.forEach(add(-1));
newA.forEach(add(1));
}
var count = { a: 0, b: 0 };
updateCount(['a'], ['a', 'b']);
console.log(count);

Here is a longer approach than the one proposed by Nina based on the same idea which illustrates some basic usage of math vectors teached in highschool..
You can do as you would in math with vectors, say in N^2
consider the vector CD
The difference is simply D-C.
Your initial point is [countA, countB]
The first component of C represents 'a' and second component represents 'b'.
So first thing: map your vector to a N^2 point:
let arrToPoint = v=>v.reduce((point, el)=>{
point[el]++;
return point;
},{a:0, b:0});
do the diff like you would for a vector
function diff(C, D){
let v = {};
Object.keys(D).forEach(k=>{
v[k] = D[k]-C[k];
});
return v;
}
add the "diff" vector to your point
function update(point, v){
Object.keys(v).forEach(k=>{
point[k] += v[k];
})
}
then your standard code
let arrToPoint = v=>v.reduce((point, el)=>{
point[el]++;
return point;
},{a:0, b:0});
function diff(C, D){
let v = {};
Object.keys(D).forEach(k=>{
v[k] = D[k]-C[k];
});
return v;
}
function update(point, v){
Object.keys(v).forEach(k=>{
point[k] += v[k];
})
}
function checkAndUpdateCount(old, next){
let v = diff(arrToPoint(old), arrToPoint(next));
update(X, v);
console.log(X);
}
let X = {a:4, b:5}
const old1 = ['a']; const new1 = ['a', 'b'];
const old2 = ['a']; const new2 = ['b'];
const old3 = ['a', 'b']; const new3 = ['b'];
const old4 = ['b', 'a']; const new4 = ['a'];
const old5 = ['b', 'a']; const new5 = [];
const old6 = []; const new6 = ['b', 'a'];
checkAndUpdateCount(old1, new1); // this will return 4 6
X.a = 4; X.b = 5;
checkAndUpdateCount(old2, new2); // 4 6
X.a = 4; X.b = 5;
checkAndUpdateCount(old3, new3); // 3 6
X.a = 4; X.b = 5;
checkAndUpdateCount(old4, new4); // 4 4
X.a = 4; X.b = 5;
checkAndUpdateCount(old5, new5); // 3 4
X.a = 4; X.b = 5;
checkAndUpdateCount(old6, new6); // 5 6
note that you can do the diff and the update in one pass, just left it as is for illustration purpose

Related

Array by percentage of the previous value [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
Improve this question
I have an array of strings, amount and a percentage.
Every key is found in the object.
Its value is equal to the previous value plus the percentage.
This is the code I wrote:
const keys = ['a', 'b', 'c'];
const amount = 300;
const precentage = 0.5;
const obj = {};
let prevAmount = amount;
for (let i = 0; i < keys.length; i++) {
if (i !== 0) {
obj[keys[i]] = prevAmount + prevAmount * precentage;
prevAmount = prevAmount + prevAmount * precentage;
} else {
obj[keys[i]] = amount;
prevAmount = amount;
}
}
The obj is:
{a: 300, b: 450, c: 675}
I don't like this code. It looks too long for me.
Is there a way to make it more readable and short?
you can do it using reduce
const createObj = (keys, amount, percentage) => keys.reduce((res, key, i) => {
if (i === 0)
return res
return {
...res,
[key]: res[keys[i - 1]] * (1.0 + percentage)
}
}, {
[keys[0]]: amount
})
const keys = ['a', 'b', 'c'];
const amount = 300;
const precentage = 0.5;
const result = createObj(keys, amount, precentage)
console.log(result)
Maybe use reduce with an accumulator that consists of the accumulated object and the accumulated amount?
const keys = ['a', 'b', 'c'];
const amount = 300;
const percentage = 0.5;
const obj = keys.reduce(({obj, amount}, key) => (
obj[key] = amount, {obj, amount: amount * (1 + percentage)}
), {obj: {}, amount}).obj;
console.log(obj); //{a: 300, b: 450, c: 675}
Here I didn't use a separate variable to store the prevAmount.
You can grab it from the object itself.
First assign the initial amount to the object as it does not change.
Then iterate the key array from the index 1 not from the index 0.
const keys = ["a", "b", "c"];
const amount = 300;
const precentage = 0.5;
const obj = {};
obj[keys[0]] = amount;
for (let i = 1; i < keys.length; i++) {
obj[keys[i]] = obj[keys[i - 1]] * (precentage + 1);
}
console.log(obj);
You can use Array.prototype.reduce and reduce the keys array into the desired object.
const
keys = ["a", "b", "c"],
amt = 300,
percentage = 0.5,
res = keys.reduce((r, k, i, a) => ((r[k] = i > 0 ? r[a[i - 1]] * (1 + percentage) : amt), r), {});
console.log(res);
Another way:
const keys = ['a', 'b', 'c'];
const amount = 300;
const precentage = 0.5;
const obj = {};
keys.reduce((prev, current, index) => {
obj[current] = prev + (index == 0 ? 0 : amount * precentage)
return (obj[current])
}, amount)
console.log(obj)
You can simply track the previous amount and update it on each iteration.
const keys = ['a', 'b', 'c'];
const amount = 300;
const percentage = 0.5;
const result = {};
let prev = amount;
for (const key of keys) {
result[key] = prev;
prev = prev + (prev * percentage);
}
console.log(result)
this could be another solution.
const keys = ['a', 'b', 'c'];
const amount = 300;
const percentage = 0.5;
function makeObj(keys, amount, percentage) {
const result = {
[keys[0]]: amount,
};
keys.forEach((k, i, arr) => {
if (i === 0) return;
const prevValue = result[arr[i - 1]];
result[k] = prevValue + prevValue * percentage;
});
return result;
}
makeObj(keys, amount, percentage);

Is there any way to sum all sub array item and then multiply all sub arrays using a 'for' or 'for-of' loop?

i want to create a function using for() or for of() loop, which take an nested array as argument then add and multiply its item.
Suppose, myArray = [[5,6],[9,2],[4,8]]
Now i want to process it like: [[5+6] * [9+2] * [4+8]]
I solve it using .map() and .reduce(), but is there any way to do same using classic for() or for of() loop. this is my trial.
let myArray = [[1,2],[3,4],[5,6]]
function multyPlus(array) {
resul = 0
for (const subArray of array) {
for (const num of subArray) {
resul += num
}
resul *= subArray
}
return resul
}
console.log(multyPlus(myArray));
//Nan
I would try a two step system that first adds the numbers, then multiplies it to the previous numbers:
function sum(array) {
var total = 0;
for (var item of array)
total += item;
return total;
}
var myArray = [[5,6],[9,2],[4,8]];
var output = 1;
for (var item of myArray)
output *= sum(item);
Maybe Like This:
let myArray = [[1,2],[3,4],[5,6]]
function multyPlus(_array){
var out = 1;
for(var key1 in _array){
var out2 = 0;
for(var key2 in _array[key1]){
out2 += _array[key1][key2];
}
out = out * out2;
}
return out;
}
console.log(multyPlus(myArray));
You can define separate adder and multiplier functions -
const adder = (nums = []) =>
{ let r = 0
for (const n of nums)
r += n
return r
}
const multiplier = (nums = []) =>
{ let r = 1
for (const n of nums)
r *= n
return r
}
const myCalc = (input = []) =>
{ const r = []
for (const x of input)
r.push(adder(x))
return multiplier(r)
}
const result =
myCalc([[1,2],[3,4],[5,6]])
console.log(result) // 231
That said, I think the functional approach is superior when you use named functions. Each function is highly reusable and there's virtually no room for bugs to hide -
const add = (x = 0, y = 0) =>
x + y
const mult = (x = 0, y = 0) =>
x * y
const sum = (nums = []) =>
nums.reduce(add, 0)
const product = (nums = []) =>
nums.reduce(mult, 1)
const myCalc = (input = []) =>
product(input.map(sum)) // <-- easy peasy!
const result =
myCalc([[1,2],[3,4],[5,6]])
console.log(result) // 231
If you have something against map and reduce, you can write myCalc and sum by hand using simple recursion -
const sum = ([ x, ...more ]) =>
x === undefined
? 0
: x + sum(more)
const myCalc = ([ x, ...more ]) =>
x === undefined
? 1
: sum(x) * myCalc(more)
const result =
myCalc([[1,2],[3,4],[5,6]])
console.log(result) // 231

Mixing Arrays using JavaScript

What's the best way to mix multiple arrays like the way in the image below,
PS:
I don't know what will be the length of each array
Arrays will contain +10000 elements
There will be more than 3 arrays
I made a solution for it but I'm looking for any better solution
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
You could use lodash for your solution.
const { flow, zip, flatten, filter} = _
const doTheMagic = flow(
zip,
flatten,
filter
)
const one = [1, 2, 3]
const two = ['😕', '🤯', '🙈', '🙊', '🙉', '😃']
const three = ['foo', 'bar', 'wow', 'okay']
const result = doTheMagic(one, two, three)
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Works with different length of arrays and makes use of functional programming for elegant code.
Here's a codepen to run: https://codepen.io/matteodem/pen/mddQrwe
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
let a1 = [1, 2, 3, 4, 5];
let a2 = ["🏯", "🏜", "🏭", "🎢", "🌠", "🏗"];
let a3 = ['one', 'two', 'three', 'four', 'five'];
const doTheMagic = arrayOfArrays => {
let maxLength = 0;
let result = [];
for (arr in arrayOfArrays) {
maxLength = Math.max(maxLength, arrayOfArrays[arr].length);
}
for (let i = 0; i < maxLength; i++) {
for (let j = 0; j < arrayOfArrays.length; j++) {
if (arrayOfArrays[j][i]) {
result.push(arrayOfArrays[j][i]);
}
}
}
return result;
}
console.log(doTheMagic([a1, a2, a3]));
This works with an unknown number of arrays, each one of unknown length :
const arrays = [
[1, 2, 3, 4],
["a", "b", "c", "d", "e"],
["#", "#", "?"]
];
let output = [];
while (arrays.some(a => a.length)) { // While any of the arrays still has an element in it, keep going
for (let a of arrays) {
if (!a.length) continue;
output.push(a.shift()); // remove the first element of the array and add it to output
}
}
console.log(output)
This is my approach to achieve that, one for loop can make it. This will work if you don't know the number of arrays and array length as well.
function doTheMagic(arr){
let ans = [];
let maxLength = -1;
arr.forEach((tempArr)=>{
if(tempArr.length > maxLength){
maxLength = tempArr.length;
}
})
let l1=0,l2=0,l3=0;
for(let i=0;i<maxLength;i++){
arr.forEach((tempArr)=>{
if(tempArr[i]){
ans.push(tempArr[i]);
}
})
}
return ans;
}
let a1 = [1,2,3,4,5];
let a2 = ["🏯","🏜","🏭","🎢","🌠","🏗"];
let a3 = ['1','2','3','4','5'];
console.log(doTheMagic([a1,a2,a3]));
Not sure if this is the better, but how you can write code that handles any number of arrays passed in.
const weave = (...args) => // convert arguments to an array
args.reduce((res, arr, offset) => { // loop over the arrays
arr.forEach((v, i) => res[offset + i * args.length] = v) // loop over array and add items to their indexes
return res
}, []).filter(x => x !== undefined) // remove the unused indexes
const one = [1, 2, 3]
const two = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
const three = ['w', 'x', 'y', 'z']
const result = weave(one, two, three)
console.log(result)
const result2 = weave(one, two)
console.log(result2)
const result3 = weave(one, two, three, ['*', '&'])
console.log(result3)

JavaScript: performance improvement to find the common elements in two array

I have a function for search the longest common elements in two array:
/**
* Return the common elements in two array
*/
function searchArrayInArray(array1, array2) {
var result = [];
for (var j = 0, e = array1.length; j < e; j++){
var element = array1[j];
if( array2.indexOf(element) !== -1 ){
result.push(element);
}
}
return result;
}
This method works, but I want improve performance because I call it many times.
There is any performance improvement appliable?
Side note: the elements into the arrays are unsorted string
/**
* Return the common elements in two array
*/
function searchArrayInArray(array1, array2) {
var result = [];
for (var j = 0, e = array1.length; j < e; j++){
var element = array1[j];
if( array2.indexOf(element) !== -1 ){
result.push(element);
}
}
return result;
}
var result = searchArrayInArray(['a', 'b'], ['b', 'c']);
document.getElementById("result").innerHTML = JSON.stringify(result, null, 2);
<pre id="result"></pre>
If you're concerned about performance, you'll want to use a data structure which provides good look-up times. Array methods like Array.prototype.indexOf, Array.prototype.includes, and Array.prototype.find all have linear look-ups. Map has binary look-up and Set has constant look-up. I think Set will be ideal in this situation.
A straightforward implementation of intersection -
const intersection = (a1 = [], a2 = []) =>
{ const s =
new Set(a1)
const result =
[]
for (const x of a2)
if (s.has(x))
result.push(x)
return result
}
console.log(intersection(['a', 'b'], ['b', 'c']))
// [ 'b' ]
This can be simplified a bit using higher-order functions like Array.prototype.filter -
const intersection = (a1 = [], a2 = []) =>
{ const s =
new Set(a1)
return a2.filter(x => s.has(x))
}
console.log(intersection(['a', 'b'], ['b', 'c']))
// [ 'b' ]
This concept can be expanded upon to support intersecting an arbitrary number of arrays -
const intersection = (a1 = [], a2 = []) =>
{ const s =
new Set(a1)
return a2.filter(x => s.has(x))
}
const intersectAll = (a = [], ...more) =>
more.reduce(intersection, a)
console.log(intersectAll(['a', 'b'], ['b', 'c'], ['b', 'd'], ['e', 'b']))
// [ 'b' ]
Well indexOf() is O(n) so by using Set() instead you can improve complexity from O(n^2) to O(n * log n)
function searchArrayInArray(array1, array2) {
var result = [];
let set = new Set();
for(el of array2){
set.add(el);
}
for (var j = 0, e = array1.length; j < e; j++){
var element = array1[j];
if( set.has(element) ){
result.push(element);
}
}
return result;
}
The easiest way:
var a = [1,2,3,4,5,6,7,8,9,10];
var b = [2,4,5,7,11,15];
var c = a.filter(value => b.includes(value))
console.log(c)

Pure js arrays merge where elements alternate [duplicate]

This question already has answers here:
Merge two arrays with alternating values
(14 answers)
Closed 4 years ago.
I found this question but it is closed, author narrowed it to jQuery, and answers are only for case when two arrays has equal size.
So my question is how to merge two arbitrary arrays where elements alternate? (in answer provide function m(a,b) which take two arrays a and b and return merged array)
Test cases:
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
var m = (a,b) => "...magic_here...";
m(as,b); // -> [1,"a",2,"b",3,"c","d","e"]
m(am,b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
m(al,b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
You can travel all elements and add it to result.
const as = [1, 2, 3];
const am = [1, 2, 3, 4, 5];
const al = [1, 2, 3, 4, 5, 6, 7];
const b = ["a", "b", "c", "d", "e"];
function m(a, b) {
const l = Math.max(a.length, b.length);
const result = [];
for (let i = 0; i < l; i++) {
if (a[i] !== undefined) {
result.push(a[i]);
}
if (b[i] !== undefined) {
result.push(b[i]);
}
}
console.log(result);
return result;
}
m(as, b); // -> [1,"a",2,"b",3,"c","d","e"]
m(am, b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
m(al, b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
A very simple is to loop over and check if value exists. If yes, push else continue.
Solution 1
function alternateMerge(a1, a2) {
var length = Math.max(a1.length, a2.length);
var output = [];
for(var i = 0; i< length; i++) {
if (!!a1[i]) {
output.push(a1[i])
}
if (!!a2[i]) {
output.push(a2[i])
}
}
return output;
}
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
console.log(alternateMerge(as, b).join())
console.log(alternateMerge(am, b).join())
console.log(alternateMerge(al, b).join())
Solution 2
function alternateMerge(a1, a2) {
const arr = a1.length > a2.length ? a1 : a2;
return arr.reduce((acc, _, i) => {
!!a1[i] && acc.push(a1[i]);
!!a2[i] && acc.push(a2[i]);
return acc;
}, [])
}
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
console.log(alternateMerge(as, b).join())
console.log(alternateMerge(am, b).join())
console.log(alternateMerge(al, b).join())
You can use array#concat with spread syntax to generate the array merged alternatively.
var m = (a,b) => {
const minLen = Math.min(a.length, b.length);
return [].concat(...a.slice(0, minLen).map((v,i) => [v, b[i]]), a.slice(minLen, a.length), b.slice(minLen, b.length));
};
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
console.log(m(as,b)); // -> [1,"a",2,"b",3,"c","d","e"]
console.log(m(am,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
console.log(m(al,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
You can do:
const as = [1,2,3];
const am = [1,2,3,4,5];
const al = [1,2,3,4,5,6,7];
const b = ["a","b","c","d","e"];
const m = (a, b) => (a.length > b.length ? a : b)
.reduce((acc, cur, i) => a[i] && b[i] ? [...acc, a[i], b[i]] : [...acc, cur], []);
console.log(m(as,b)); // -> [1,"a",2,"b",3,"c","d","e"]
console.log(m(am,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
console.log(m(al,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories