Find the opposite number in the array - javascript

I'm trying to find the opposite number in the array.
Let's say we have this array: [1,3,4,-3]
What I want is:
{ '1': 1, '3': 2, '4': 1 } // as we have two 3
and what I tried is :
const arr = [1,3,4,-3]
let holder = {}
arr.map(a => {
holder[a] = (holder[a] ||0)+1
})
console.log(holder)
Any idea how can I acheive this ?

The rules can be summed up as follow:
Let v be the array
Let x a positive number in the array
if x is in v but not -x, keep {[x]:1}
if x is in v and -x also, keep {[x]:2}
Let y a negative number in the array
if y is in v and -y also do nothing (symetry of 2.)
if y is in v but not -y, keep {[y]:1}
Now it is pretty straightforward:
build a set of values of v
check all the positive keys
foreach one, check if there exists its opposite and apply 1. or 2.
delete any opposite keys
for the remaining keys (which are then negative and have no opposite) apply 3.
const v = [1,1,1,-2, 3,3,-3, 4,-4,-4]
const output = {}
const s = new Set(v)
const positives = [...new Set(v.filter(x => x>= 0))]
positives.forEach(p => {
if (s.has(-p)) {
output[p] = 2
s.delete(-p)
} else {
output[p] = 1
}
s.delete(p)
})
;[...s].forEach(negative => {
output[negative] = 1
})
console.log('output', output)
Now we can be a bit more aggressive regarding the symetry:
if x === -y
upon iterating x, setting {keep[x]: 2}
then upon iterating -y, setting {keep[-y]: 2} is equivalent to setting {keep[x]: 2} which has already been done.
So we may or may not set {keep[-y]: 2}, this is equivalent
const v = [1,1,1,-2, 3,3,-3, 4,-4,-4]
const output = {}
const s = new Set(v)
;[...s].forEach(x => {
if (s.has(-x)) {
output[Math.abs(x)] = 2
} else {
output[x] = 1
}
})
console.log('output', output)

Not quite sure if this is really what you trying to achieve. If so, you can do it with a reduce. Please check:
const arr = [1, 3, 4, -3];
const obj = arr.reduce((acc, cur) => ({
...acc,
[Math.abs(cur)]: (acc[Math.abs(cur)] || 0) + 1,
}), {});
console.log(obj);

const arr = [1,3,4,-3];
const allPositive = arr.map(item => Math.abs(item));
const reducer = (acc, item, index) => {
const count = allPositive.filter(x => x===item).length;
acc[item] = count;
return acc
};
const result = allPositive.reduce(reducer, {});
console.log(result)

Related

How can I create a numeric array based on the number of array objects in React?

I have an array object called members?.admins. I want to create an array of numbers based on the length of members?.admins.
As an exception, if the length of members?.admins is 1 to 5, [1] should be given as the default value. That is, the default value must be unconditionally [1].
If the number of members?.admins is 5, I want to make [1] in numbers, and if there are 10, I want to make [1,2]. Also, if there are 11, it should be [1,2]
At this time, I want to use useEffect to setNumbers on the first render to create an array of numbers in numbers.
How can I do that?
const members?.admins = [{memberId:"21",name:"jack21"},{memberId:"20",name:"jack20"},{memberId:"14",name:"jack14"},{memberId:"13",name:"jack13"},{memberId:"11",name:"jack11"},{memberId:"10",name:"jack10"},{memberId:"7",name:"jack7"},{memberId:"4",name:"jack4"},{memberId:"3",name:"jack3"},{memberId:"2",name:"jack2"},{memberId:"1",name:"jack1"}];
const [numbers, setNumbers] = useState([1]);
useEffect(() => {
setNumbers();
}, []);
Expected output:
if members?.admins.length = 1~5
it shuld be
number = [1]
if members?.admins.length = 6~10
it shuld be
number = [1,2]
if members?.admins.length = 11~15
it shuld be
number = [1,2,3]
let members = {admins: [1,2,3,4,5,6,7,8,9,10]}
console.log(Array(Math.ceil((members?.admins.length || 0) / 5)).fill(undefined).map((el, i) => i + 1))
let admins = ["a","b","c","d","e","f","g"];
let result = Array.from({length: Math.ceil(admins.length / 5)}, (_, i) => i + 1);
console.log(result);
You can use Math.ceil to get the required length and then fill the array later.
const members = {
admins: Array(6)
}
const reqLength = Math.ceil(members?.admins?.length / 5);
const reqArray = Array(reqLength).fill().map((v, i) => i + 1);
console.log(reqArray);
Here you can create array using length
useEffect(() => {
const array = [...Array(Math.ceil( members?.admins.length / 5))].map((_, i)=> i+1)
setNumbers(array);
}, []);
const members?.admins = [{memberId:"21",name:"jack21"},{memberId:"20",name:"jack20"},{memberId:"14",name:"jack14"},{memberId:"13",name:"jack13"},{memberId:"11",name:"jack11"},{memberId:"10",name:"jack10"},{memberId:"7",name:"jack7"},{memberId:"4",name:"jack4"},{memberId:"3",name:"jack3"},{memberId:"2",name:"jack2"},{memberId:"1",name:"jack1"}];
const [numbers, setNumbers] = useState([1]);
useEffect(() => {
let number = [];
let mb_length = members?.admins.length;
if(mb_length%5 === 0){
let mbb = (mb_length - mb_length%5)/5;
number = Array.from({length: mbb}, (_, i) => i + 1)
}else{
let mbb = (mb_length - mb_length%5)/5;
number = Array.from({length: mbb+1}, (_, i) => i + 1)
}
setNumbers(number);
}, []);

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));

How do I write all iterations of reduce to an array?

The idea behind the script is as follows:
I am filtering the original array by removing all even values from it. Next, I form a new array from the partial products of the original array:
// new first element = 1 * 3;
// second = (first (previous multiplication) * current value) = 3 * 3 (9);
// third = (previous product * current value) = 9 * 9 (81)
I got the results I wanted, but my output doesn't look like an array:
Input:
3 3 9
Output:
3
9
81
Please help me draw the following output:
Input:
3 3 9
Output:
3 9 81
function modify(arr) {
var result = [];
arr = arr.filter(item => !(item % 2 == 0))
.reduce(function(acc, curItem) {
console.log(acc * curItem);
return acc * curItem;
}, 1)
for (let i = 0; i < result.length; i++) {
arr.push(result[i]);
};
return result;
}
console.log( modify([3,3,9]) )
You can use reduce keeping track of both your cumulative product, and the final array:
const input = [3, 3, 9];
const output = input.reduce( (acc,val) => {
const newResult = acc.cum * val;
acc.result.push(newResult);
acc.cum = newResult;
return acc;
},{cum:1, result:[]});
console.log(output.result);
As the last element of the array is always the cumulative product, you could also write it like this:
const input = [3, 3, 9];
const output = input.reduce( (acc,val) => {
const newResult = (acc.length ? acc[acc.length-1] : 1) * val;
acc.push(newResult);
return acc;
},[]);
console.log(output);
It's up to you which one of these you find easier to work with.
With Array#reduce() method and the spread operator you can transform your array as in the following demo:
let modify = (arr) => arr.reduce((acc,cur,i) => [...acc, (acc[i-1] || 1) * cur],[]);
console.log( modify([3,3,9]) );
Or simply:
function modify(arr) {
return arr.reduce(function(acc,cur,i) {
return [...acc, (acc[i-1] || 1) * cur]
}, []);
}
console.log( modify([3,3,9]) );
Or if this is code that runs once, in which case you don't need a function:
let oldArray = [3,3,9];
let newArray = oldArray.reduce(function(acc,cur,i) {
return [...acc, (acc[i-1] || 1) * cur]
}, []);
console.log( newArray );

Javascript - Delete all duplicates from array

I have problem with delete all duplicate in array.
Array = [1,1,2,2,3]
Every solution, what I found, haves result this
Array = [1,2,3]
But I need this
Array = [3]
How can I do this?
You can first iterate over the array once to obtain a Map of the frequencies of each item and then filter to find the elements that only appeared once.
const arr = [1,1,2,2,3];
const freq = arr.reduce((acc,curr)=>(acc.set(curr,(acc.get(curr)||0)+1),acc),new Map);
const res = arr.filter(x => freq.get(x) === 1);
console.log(res);
You could store an object for occurences of each element and get the elements that have the occurence of 1
const arr = [1, 1, 2, 2, 3]
const occurrences = arr.reduce((acc, el) => {
acc[el] = (acc[el] || 0) + 1
return acc
}, {})
const res = Object.entries(occurrences)
.filter(([el, time]) => time === 1)
.map(([el]) => +el)
console.log(res)
Unlike some of the other solutions, this allows you to make a single loop over the array, rather than a reduce followed by a filter and/or map loop. That said, there are trade-offs in readability and other condition checks, and it plays a bit fast and loose with the semantic intention of a reduce, so it might be a wash in terms of benefits.
const myArray = [1,1,2,2,3];
const dupesRemoved = myArray.reduce((acc, cur, idx, src) => {
if (!acc.dupes.has(cur)) {
if (acc.singleInstances.has(cur)) {
acc.singleInstances.delete(cur);
acc.dupes.add(cur);
} else {
acc.singleInstances.add(cur);
}
}
if (idx === src.length - 1) {
return [...acc.singleInstances];
}
return acc;
}, { singleInstances: new Set(), dupes: new Set() });
console.log(dupesRemoved);
Here is a simple and short solution:
let arr = [1,1,2,2,3];
let filtered_arr = arr.filter(v => arr.indexOf(v) === arr.lastIndexOf(v));
console.log(filtered_arr);

List traversal with two adjacent elements in a Functional way

I'm trying to implement functional version of the below code
const adjacent = (list) => {
let results = [];
for (let idx = 0; idx < list.length - 1; idx++) {
const computedRes = someComplexFn(list[idx], list[idx + 1]);
results.push(computedRes );
}
return results;
}
i have come with the following version
const locations = [1,2,3,4,5];
const calcRatioFn = (x, y) => x+y;
const adjacentMap = (list, result=[]) => {
if(R.length(list) < 2) {
return result;
}
const f1 = R.head(list);
const f2 = R.tail(list);
result.push(calcRatioFn(f1 ,R.head(f2)));
return adjacentMap(R.tail(list), result);
}
const results = adjacentMap(locations);
console.log(results);
Are any any other simple solution to the above?
Can we avoid the default result value parameter and if condition check from the above function?
JSBin Link
http://jsbin.com/veyihepulu/1/edit?html,js,console
One approach would be to create a sliding window of adjacent elements using R.aperture. Then for a bit of extra sugar someComplexFn can be wrapped with R.apply to convert the binary function into one that accepts an array of two elements.
Your example would then look something like:
const adjacentMap = R.pipe(R.aperture(2), (R.map(R.apply(someComplexFn))))
Another approach would be to use converge on the array without the last element and the array without the first element.
let locations = [1,2,3,4,5];
const calcRatio = (x, y) => x+y;
// adjacentMap :: Array -> Array
const adjacentMap = R.converge(
R.zipWith(calcRatio),
[ R.init, R.tail]
);
// saveAdjacentMap :: Array -> Array
const saveAdjacentMap = R.cond([
[R.compose(R.lt(1), R.length), adjacentMap ],
[R.T, R.identity]
]);
console.log(saveAdjacentMap(locations));
Your JSBin uses Ramda 0.8.0. Things have changed in the current version 0.24.1.
The following code maybe what you require or can be adapted for your required solution.
const fn = (acc, c, i, a) => {
return !(a[i + 1])
? acc
: acc.concat(c + a[i + 1])
}
const _adjacentMap = (fn, list) => {
return list.reduce(fn, [])
}
const locations = [1,2,3,4,5]
const result = _adjacentMap(fn, locations)
console.log(result)
// => [ 3, 5, 7, 9 ]

Categories

Resources