JavaScript array or object checking - code improvements - javascript

I receive data => these data could be array of object or just a object.
I write some code, but maybe there is a way to make this code more sexy, clear, or shorter plus without any errors
Here is the code:
export const CalculateIt = (props) => {
const conversionValues = []
if (props) {
if (props.length > 0) {
for (let i = 0; i < props.length; i++) {
const clicks = props[i]?.insights?.[0].inline_link_clicks
const actionsNumber = props[i]?.insights?.[0]?.actions?.length || 0
let result = 0
if (clicks && actionsNumber) {
result = devideNumbers(clicks, actionsNumber, 8)
}
conversionValues.push(result)
}
return conversionValues
}
const clicks = props?.insights?.[0].inline_link_clicks
const actionsNumber = props?.insights?.[0]?.actions?.length || 0
let result = 0
if (clicks && actionsNumber) {
result = devideNumbers(clicks, actionsNumber)
}
return conversionValues.push(result)
}
}
As you can see there you can find some parts of the code that are similar like:
const clicks = props[i]?.insights?.[0].inline_link_clicks
and
const clicks = props?.insights?.[0].inline_link_clicks
Is it possible to write it more smart?
Best

Probably move the common code in a function:
function getResult(data) {
const clicks = data?.insights?.[0].inline_link_clicks
const actionsNumber = data?.insights?.[0]?.actions?.length || 0
let result = 0
if (clicks && actionsNumber) {
result = devideNumbers(clicks, actionsNumber, 8)
}
return result;
}
And use the helper function in your original function:
export const CalculateIt = (props) => {
const conversionValues = []
if (props) {
if (props.constructor === Array) {
props.forEach((item) => conversionValues.push(getResult(item)))
} else {
conversionValues.push(getResult(props));
}
return conversionValues;
}
}

In fact, you can force all data into a one-dimensional array and safe work with array of objects only.
Is this code sexy enough?
const obj = {id: 1, val: 1};
const arr = [{id: 1, val: 1},{id: 2, val: 2},{id: 3, val: 3}];
const normalize = (data) => [data].flat();
console.log(normalize(obj)[0]);
console.log(normalize(arr)[0]);
// -------------------
// and you can use this approach in code:
const getResult = (obj) => obj.val * 10;
const CalculateIt = (props) => [props].flat().map(getResult);
console.log(CalculateIt(obj));
console.log(CalculateIt(arr));
.as-console-wrapper{min-height: 100%!important; top: 0}

Related

Rank Order Voting function returning the wrong output

I am building a function that count Rank Order Ballots and returns the winner. The rules of this are that if a candidate has a clear majority then the candidate wins the election.
If not, we remove all reference to that candidate and whichever ballots the candidate go are assigned to whoever came second
So for example if we have this
const sample = { "A,B,C": 4, "B,C,A": 3, "C,B,A": 2};
Since C has the least number of votes and noone has a majority, all votes C won are then assigned to B, giving B the majority.
This is what I have written:
function removeLowestVotedCandidate(ballots) {
let lowestVotes = Object.entries(ballots).reduce((largestVal, comparison) =>comparison[1] < largestVal[1] ? comparison
:largestVal)[0]
.split('')[0]
//remove lowest voted candidate from object
const newRankedOrderBallots = JSON.parse(
JSON.stringify(ballots)
.replaceAll(`${lowestVotes}`, '')
.replaceAll(/(^[,\s]+)/g, '')
)
//remove leading commas
return Object.fromEntries(Object.entries(newRankedOrderBallots).map(([key, value]) => [key.replace(/^,+/,''), value]))
}
// console.log(removeLowestVotedCandidate(sample))
function getRankedChoiceWinner(ballots) {
let sum = 0
let winnerFound = false
let stretchWin = 0;
let sumByChar = {};
let winner = []
let updatedBallot = ballots
while(winnerFound === false) {
//count overall votes
for(let votes of Object.values(updatedBallot)){
sum +=votes
}
//calculate what is required for a clear majority
stretchWin = Math.round(sum/2)
//count votes assigned to each candidate
for(const[key, val] of Object.entries(updatedBallot)) {
const char = key[0];
sumByChar[char] = (sumByChar[char] ?? 0) + val;
}
console.log('sumByChar is currently', sumByChar)
//check if any candidate has a clear majority
winner = Object.entries(sumByChar)
.filter(([, val]) => val >= stretchWin)
.map(([keys]) => keys)
console.log('winner is currently', winner)
if (winner.length === 1) {
winnerFound = true
} else {
updatedBallot = removeLowestVotedCandidate(updatedBallot)
console.log('we are inside else', updatedBallot)
}
}
return winner
}
However, I seem to be getting the wrong answer, I am getting A as opposed to B. This is what is happening with my console.logs
sumByChar is currently { A: 4, B: 3, C: 2 }
winner is currently []
we are inside else { 'A,B,': 4, 'B,,A': 3, 'B,A': 2 }
sumByChar is currently { A: 8, B: 6, C: 4 }
winner is currently []
we are inside else { 'A,,': 4, A: 2 }
sumByChar is currently { A: 12, B: 9, C: 6 }
winner is currently [ 'A' ]
[ 'A' ]
It seems sumByChar is not reseting to zero and instead
There are 2 issues:
Your sumByChar is created outside the loop and mutated inside the loop. Every time there's a new iteration, you add additional values to it. Create the object inside the loop instead, so you get the sum only for the current ballots, not the cumulative sum for all iterations so far.
Your sum variable is also declared outside the loop, and you're adding to it inside the loop. Declare it inside the loop instead.
Also, the input structure is pretty badly designed for something like this. I'd highly recommend restructuring it to make removing candidates easier - using JSON.stringify and a regex just to remove something is extremely suspicious.
const sample = {
"A,B,C": 4,
"B,C,A": 3,
"C,B,A": 2
};
function removeLowestVotedCandidate(ballots) {
let lowestVotes = Object.entries(ballots).reduce((largestVal, comparison) => comparison[1] < largestVal[1] ? comparison :
largestVal)[0]
.split('')[0]
//remove lowest voted candidate from object
const newRankedOrderBallots = JSON.parse(
JSON.stringify(ballots)
.replaceAll(`${lowestVotes}`, '')
.replaceAll(/(^[,\s]+)/g, '')
)
//remove leading commas
return Object.fromEntries(Object.entries(newRankedOrderBallots).map(([key, value]) => [key.replace(/^,+/, ''), value]))
}
// console.log(removeLowestVotedCandidate(sample))
function getRankedChoiceWinner(ballots) {
let winnerFound = false
let stretchWin = 0;
let winner = []
let updatedBallot = ballots
while (winnerFound === false) {
let sumByChar = {};
//count overall votes
let sum = 0
for (let votes of Object.values(updatedBallot)) {
sum += votes
}
//calculate what is required for a clear majority
stretchWin = Math.round(sum / 2)
//count votes assigned to each candidate
for (const [key, val] of Object.entries(updatedBallot)) {
const char = key[0];
sumByChar[char] = (sumByChar[char] ?? 0) + val;
}
// console.log('sumByChar is currently', sumByChar)
//check if any candidate has a clear majority
winner = Object.entries(sumByChar)
.filter(([, val]) => val >= stretchWin)
.map(([keys]) => keys);
console.log('winner is currently', winner)
if (winner.length === 1) {
winnerFound = true
} else {
updatedBallot = removeLowestVotedCandidate(updatedBallot)
// console.log('we are inside else', updatedBallot)
}
}
return winner
}
console.log(getRankedChoiceWinner(sample));
Or, refactored to look halfway decent IMO:
const sample = {
"A,B,C": 4,
"B,C,A": 3,
"C,B,A": 2
};
const getRankedChoiceWinner = badBallots => checkOneBallot(restructureBallots(badBallots));
const restructureBallots = badBallots => Object.entries(badBallots)
.map(([candidatesStr, votes]) => [candidatesStr.split(','), votes]);
const checkOneBallot = (ballots) => {
const sumVotes = ballots.reduce((a, b) => a + b[1], 0);
const sumByCandidate = {};
for (const [candidates, voteCount] of ballots) {
const candidate = candidates[0];
sumByCandidate[candidate] = (sumByCandidate[candidate] ?? 0) + voteCount;
}
const winningEntry = Object.entries(sumByCandidate).find(([, val]) => val >= sumVotes / 2);
if (winningEntry) return winningEntry[0][0];
return removeLowestAndRetry(ballots, sumByCandidate);
};
const removeLowestAndRetry = (ballots, sumByCandidate) => {
const lowestVal = Math.min(...Object.values(sumByCandidate));
const lowestCandidateEntry = Object.entries(sumByCandidate).reduce(
(a, entry) => entry[1] < a[1] ? entry : a,
['', Infinity]
);
const lowestCandidate = lowestCandidateEntry[0];
for (const ballot of ballots) {
ballot[0] = ballot[0].filter(candidate => candidate !== lowestCandidate);
}
return checkOneBallot(ballots);
};
console.log(getRankedChoiceWinner(sample));

Array of functions with condition

I am currently working on an array of functions and want to get the right function back with a condition.
Here is my code :
{'key':'a', 'function':func_a},
{'key':'b', 'function':func_b},
{'key':'c', 'function':func_c},
{'key':'d', 'function':func_d}
];
const term = 'b';
const funcToDo = for (var i = 0; i < array.length; i++) {
if (term === a[i].key) {
return a[i].function
}
}
const shouldIDoIt = true;
shouldIDoIt === true ? functToDo() : null;
Can someone help me on this ?
Use Array.prototype.find to then return from that array an Object which matches a specific property value
const find = (arr, k, v) => arr.find(ob => ob[k] === v);
const func_a = () => console.log("aaa!");
const func_b = () => console.log("bbb!");
const func_c = () => console.log("ccc!");
const arrFn = [
{key: "a", function: func_a},
{key: "b", function: func_b},
{key: "c", function: func_c},
];
const funcToDo = find(arrFn, "key", "b")?.function;
const shouldIDoIt = true;
funcToDo && shouldIDoIt && funcToDo();

What is the most performant approach to return an array of matching elements between 2 objects in javascript?

Given the following 2 objects in javascript:
myFruit = {
'apple': 14,
'orange': 3,
'pear': 10
}
theirFruit = {
'banana': 10,
'grape': 30,
'apple': 2
}
What would be the most performant way to return an array of matching elements? The value for each of the keys does not matter.
Below is one example, but something tells me there is probably a better approach.
let matches = [];
let myKey;
Object.keys(myFruit).forEach((key, index) => {
myKey = key;
Object.keys(theirFruit).forEach((theirKey, index) => {
if(myKey === theirKey) {
matches.push(theirKey);
}
});
});
console.log(matches);
// will print: ['apple']
console.log(matches.length);
// will print: 1
Here is my solution.
const matches = Object.keys(myFruit).filter(key => key in theirFruit);
console.log(matches); // will output ['apple']
whether or not the 2 objects contain a matching key
If all keys are different, then a merged object will have as many keys as each object individually.
let haveAMatchingKey = Object.keys(Object.assign({}, myFruit, theirFruit)).length !=
Object.keys(myFruit).length + Object.keys(theirFruit)
After edit:
the most performant way to return an array of matching elements?
let myFruitSet = new Set(Object.keys(myFruit));
let theirFruitKeys = Object.keys(theirFruit);
let matchingKeys = theirFruitKeys.filter(fruit => myFruitSet.has(fruit))
Using HashMap Data Structure approach:
const findCommonFruits = () => {
const myFruit = {
'apple': 14,
'orange': 3,
'pear': 10
}
const theirFruit = {
'banana': 10,
'grape': 30,
'apple': 2
}
// #1 select lowest object keys
let lowestObj = null;
let biggestObj = null;
if (Object.keys(myFruit).length < Object.keys(theirFruit).length) {
lowestObj = myFruit;
biggestObj = theirFruit;
} else {
lowestObj = theirFruit;
biggestObj = myFruit;
}
// 2 Define an actual hashmap that will holds the fruit we have seen it
const haveYouSeenIt = {};
for (let fruit of Object.keys(lowestObj)) {
haveYouSeenIt[fruit] = fruit;
}
const res = [];
for (let fruit of Object.keys(haveYouSeenIt)) {
if (biggestObj[fruit] !== undefined) {
res.push(fruit);
}
}
return res;
}
console.log(findCommonFruits()); // ['apple']

Test for equality of arrays

How in the test to compare each value of the array, and not the entire array?
In my test, I compared the standardArray, but I need to compare List [1,2,3,4], but I'll get it so that the test does not lose its meaning.
Maybe somehow by the indexes or otherwise ...
import { List, Set } from "immutable"
let standardArray = List([1,2,3,4]);
export function mass(standardArray) {
let mutatedArray = standardArray.map(x => x * 2);
return mutatedArray;
};
test code:
import { List, Set, isImmutable, Record, Map } from "immutable"
import { mass } from "./sum";
test('aligning arrays', () => {
let standardArray = List([1,2,3,4]);
for (let i = 0; i < 1; i++) {
expect(mass(standardArray)).toEqual(standardArray.map(x => x * 2));
};
});
If you want to check each value you can try something like this:
test('values should match', () => {
let originalArray = List([1,2,3,4]);
let expectedResult = List([2,4,6,8]);
let actuaResult = mass(originalArray);
for (let i = 0; i < originalArray.length; i++) {
expect(expectedResult[i]).toEqual(actuaResult[i]);
};
});

JavaScript Callback Confusing Behavior

Having trouble writing a callback function in JavaScript that returns a mapped array with each number in it incremented by one.
arr = [1, 2, 3, 4];
const each = (elements, cb) => {
for (let i = 0; i < elements.length; i++) {
cb(elements[i], i);
}
};
const map = (elements, cb) => {
const mappedArr = [];
each(elements, item => {
mappedArr.push(cb(item));
});
return mappedArr;
};
const cb = (e) => {
e += 1;
}
const newArr = map(arr, cb);
console.log(newArr) // [ undefined, undefined, undefined, undefined ]
Your patience is appreciated in advance; I'm still learning and trying to understand callbacks. Please help me understand what I did wrong here.
Your cb is not returning anything at the moment, so the return value defaults to undefined. Use an arrow function with implicit return (no { }s) instead, so that the incremented value is returned.
Also, try to avoid implicitly creating global variables (with your arr):
const arr = [1, 2, 3, 4];
const each = (elements, cb) => {
for (let i = 0; i < elements.length; i++) {
cb(elements[i], i);
}
};
const map = (elements, cb) => {
const mappedArr = [];
each(elements, item => {
mappedArr.push(cb(item));
});
return mappedArr;
};
const cb = e => e + 1;
const newArr = map(arr, cb);
console.log(newArr)
return in missing in cb function.Check this:
arr = [1, 2, 3, 4];
const each = (elements, cb) => {
for (let i = 0; i < elements.length; i++) {
cb(elements[i], i);
}
};
const map = (elements, cb) => {
const mappedArr = [];
each(elements, item => {
mappedArr.push(cb(item));
});
return mappedArr;
};
const cb = (e) => {
return e += 1;
}
const newArr = map(arr, cb);
console.log(newArr) // [ undefined, undefined, undefined, undefined ]

Categories

Resources