Related
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
I have the following functions that returns the intersection of the elements between some arrays of strings.
Initially it was meant to handle only 2 arrays, but I started having the need for it to handle more than 2, so I added the conditional return to make it recursive.
How can I make it flexible enough so it will be able to handle any number of arrays (equal or greater than 2, of course).
I though about using ...rest parameters, but I don't know how to do this, yet.
function intersection(list1, list2, list3, list4) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
if (list3 && list4) {
return intersection(result,list3,list4);
}
if (list3) {
return intersection(result,list3);
}
return result;
}
SNIPPET
function intersection(list1, list2, list3, list4) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
if (list3 && list4) {
return intersection(result,list3,list4);
}
if (list3) {
return intersection(result,list3);
}
return result;
}
const x1 = ['a','b','c','d','e'];
const x2 = ['a','b','c','d'];
const x3 = ['a','b','c'];
const x4 = ['a','b'];
console.log('Intersection(x1,x2,x3,x4): ' + JSON.stringify(intersection(x1,x2,x3,x4)));
console.log('Intersection(x1,x2,x3): ' + JSON.stringify(intersection(x1,x2,x3)));
console.log('Intersection(x1,x2): ' + JSON.stringify(intersection(x1,x2)));
No need for a recursion, you could take rest parameters ... and for getting the intersection a Set and filter against it.
function intersection(...arrays) {
return arrays.reduce((a, b) => a.filter(Set.prototype.has, new Set(b)));
}
console.log(intersection(['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd'], ['a', 'b', 'c'], ['a', 'b']));
console.log(intersection(['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd'], ['a', 'b', 'c']));
console.log(intersection(['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd']));
Version with recursion
function intersection(a, b = [], ...arrays) {
var i = a.filter(Set.prototype.has, new Set(b));
return arrays.length
? intersection(i, ...arrays)
: i;
}
console.log(intersection(['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd'], ['a', 'b', 'c'], ['a', 'b']));
console.log(intersection(['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd'], ['a', 'b', 'c']));
console.log(intersection(['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd']));
Nina's answer is of course greater, but I also post to propose a solution based on your own code.
As your function needs at least two lists, then precise them in your argument list. Then, use destructuring for the facultative ones.
Then, if there is at least one facultative list, re-execute your function by specifying the second parameter as the first facultative one. Use Array#shift to remove it from the list. Don't forget to put your new facultative list.
function intersection(list1, list2, ...lists) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
if (lists.length) {
return intersection(result, lists.shift(), ...lists);
}
return result;
}
const x1 = ['a','b','c','d','e'];
const x2 = ['a','b','c','d'];
const x3 = ['a','b','c'];
const x4 = ['a','b'];
console.log('Intersection(x1,x2,x3,x4): ' + JSON.stringify(intersection(x1,x2,x3,x4)));
console.log('Intersection(x1,x2,x3): ' + JSON.stringify(intersection(x1,x2,x3)));
console.log('Intersection(x1,x2): ' + JSON.stringify(intersection(x1,x2)));
You can create another function which compares two arrays and returns the intersection. In the intersection function, get the arrays passed as a 2D array called lists using Rest parameters. Use reduce to intersect the arrays with compare as the callback
function compare(list1, list2) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
return result
}
function intersection(...lists) {
return lists.reduce(compare)
}
const x1 = ['a','b','c','d','e'];
const x2 = ['a','b','c','d'];
const x3 = ['a','b','c'];
const x4 = ['a','b'];
console.log('Intersection(x1,x2,x3,x4): ' + JSON.stringify(intersection(x1,x2,x3,x4)));
console.log('Intersection(x1,x2,x3): ' + JSON.stringify(intersection(x1,x2,x3)));
console.log('Intersection(x1,x2): ' + JSON.stringify(intersection(x1,x2)));
Note: You can probably simplify the implementation of compare. This is just to demonstrate with your existing code
var a = 1;
var b = 2;
var c = 3;
var d = Math.max(a, b, c);
return d;
This code is returning 3. I need a result with not only the computed value, but with a variable name also.
Desired output: c - 3.
Unfortunately, the names of variables are not easily retrievable in JavaScript.
Such a solution is not scalable either.
However, an adjustment to the data structures that you leverage can solve this.
Note the use of Object, Object.entries() and Array.prototype.reduce() in the example below.
// Pairs.
const pairs = {
a: 1,
b: 2,
c: 3
}
// Get Max Pair.
const getmaxpair = (pairs) => Object.entries(pairs).reduce((max, pair) => !max && pair || pair[1] > max[1] ? pair : max, false)
// Max Pair.
const [key, value] = getmaxpair(pairs)
// Log.
console.log('max pair:', `${key} ${value}`) // c 3
If you are okay with storing those values in an object, you can loop through the keys and compare their values while storing the name of the key.
var values = { a: 1, b: 2, c: 3 }
var maxName;
var max;
for(var key in values) {
if(max === undefined || max < values[key]){
maxName = key;
max = values[key];
}
}
console.log('Max: ' + maxName + ' - ' + values[maxName]);
var values = { a: 1, b: 2, c: 3 }
var maxName;
var max;
for(var key in values) {
if(max === undefined || max < values[key]){
maxName = key;
max = values[key];
}
}
console.log('Max: ' + maxName + ' - ' + values[maxName]);
You can do this easily with javascript objects or maps
var list = {"a": 1, "b": 2, "c": 3};
var d = Math.max(list.a, list.b, list.c);
// this will return an array: ["a", "b", "c"]
keys = Object.keys(list);
// do a reverse lookup here to find which key your value belongs to
// eg. if list[keys[counter]] == d { your code here }
Here is another way of doing reverse lookups
https://stackoverflow.com/a/9907509/9310329
Hope this helps
Although this could be more easily achieved using a map (see other answers) in a realistic use case.
What you're asking for is actually also possible (inspired from https://stackoverflow.com/a/42791996/7988438 )
let a = 1;
let b = 2;
let c = 3;
let d = Math.max(a, b, c);
let varToString = varObj => Object.keys(varObj)[0];
if (a == d) {
console.log(varToString({a}),d)
}
if (b == d) {
console.log(varToString({b}),d)
}
if (c == d) {
console.log(varToString({c}),d)
}
Quite ugly, but gets the job done.
While the magic you think about is possible to some extent with iterating over object keys as shown in the other answers, often you just want an array of objects instead, where the keys are known, and the values vary:
var persons=[
{name: "John", age: 52},
{name: "Jane", age: 35},
{name: "Peter", age: 86},
{name: "Susan", age: 72}
];
var eldest=persons[0];
persons.forEach(function(person){
if(person.age>eldest.age)eldest=person;
});
console.log(eldest);
The question was to find the index of a rotation point, like this:
var letters = ['c', 'd', 'e', 'a', 'b']; // -> 3 because it's the index of 'a'
I was trying to do it in O(log n) time, does using slice make it O(n log n)?
Here's my solution:
var findRotationPointLogN = function(array, addedLength) {
var midpointIndex = Math.floor(array.length / 2);
var right = array.slice(midpointIndex);
var left = array.slice(0, midpointIndex);
if (array[midpointIndex] < array[midpointIndex - 1]) {
if (addedLength) {
return midpointIndex + addedLength;
} else {
return midpointIndex;
}
} else if (array[midpointIndex] > array[0]) {
if (addedLength) {
return findRotationPointLogN(right, addedLength + left.length);
} else {
return findRotationPointLogN(right, left.length);
}
} else if (array[midpointIndex] < array[0]) {
return findRotationPointLogN(left, 0);
}
}
It's not the point where the letter is smaller than the previous letter?
const r = l => l.reduceRight((p, c, i, a) => a[i-1] && a[i].charCodeAt(0) < a[i-1].charCodeAt(0) ? i : p, 0);
console.log(r(['c', 'd', 'e', 'a', 'b']));
Please see the fiddle.
I am bit stuck with why the loop does not break the way I intended.
input is an object, indexed with some kind of id. And the values are the children ids. so, I am trying to filter out (here it is hard coded as d) a node and its children. For example, for d, there is only one child, h. so, I was expecting an output ['d', 'h']. This is because, once the filter condition met (c === 'd'), it discards all previous value, and just recurse to its children (only h) in this case. Once the recursion to children finished, the break should terminate the for loop. but as I see from the output, the for loop doesn't breaks and continues with (c -> g). How to make this so once the filter met, it only output the node and all its children ['d', 'h'] in this case.
const input = {
a: ['b', 'c'],
b: ['d', 'e', 'f'],
c: ['g'],
d: ['h'],
}
let data = [];
const recur = (d) => {
const ch = input[d]
if (!ch) {
return;
}
for (let i = 0; i < ch.length; i++) {
console.log(data)
const c = ch[i]
data.push(c)
if (c === 'd') {
data = [c];
console.log("reset data", data)
recur(c)
break
}
recur(c)
}
}
recur('a')
console.log("end: ", data)
output: end: ["d", "h", "c", "g"]
This is what I came up with. https://jsfiddle.net/j6s1vouv/4/ There need a way to inform the parent that, the filter is met and should skip the sibling iteration.
const input = {
a: ['b', 'c'],
b: ['d', 'e', 'f'],
c: ['g'],
d: ['h'],
}
const recur = (d) => {
const ch = input[d]
if (!ch) {
return {
data: []
};
}
let out = [];
for (let i = 0; i < ch.length; i++) {
const c = ch[i]
const x = recur(c)
if (x.skip) { // if any of the child met filter
return x
}
if (c === 'd') { // current id met filter
return {
data: [c].concat(x.data),
skip: true
}
}
out = out.concat([c], x.data)
}
return {
data: out
}
}
console.log("end: ", recur('a').data)