Related
I would like to count all values where a letter appears first and return the letter with atleast half of all values in my object so for example I assuming I have an object like this
const sample = { "A,B,C": 4, "B,C,A": 3, "C,B,A": 2, "A,C,B": 2 };
I would return A because if you count all the values where A appears first you would get 6 (4+2)
This is what I currently have:
for (let votes of Object.values(sample)) {
sum += votes
}
stretchWin = Math.round(sum / 2)
winner = Object.entries(sample)
.filter(([, val]) => val >= stretchWin)
.map(([keys]) => keys)
With this I am getting an empty array because I am not counting all the values assigned to A
Iterate over the whole sample first to get a sum of the values by the first letter first, then iterate over that new object to identify which values match the target of half the total.
const sample = {
"A,B,C": 4,
"B,C,A": 3,
"C,B,A": 2,
"A,C,B": 2
};
const sumByChar = {};
for (const [key, value] of Object.entries(sample)) {
const char = key[0];
sumByChar[char] = (sumByChar[char] ?? 0) + value;
}
let sum = 0;
for (let votes of Object.values(sample)) {
sum += votes
}
const targetSum = Math.round(sum / 2);
const winners = Object.entries(sumByChar)
.filter(([, val]) => val >= targetSum)
.map(([key]) => key);
console.log(winners);
I'm not completely sure what you mean what the outcome should be. If I understand correctly you want something like this??
const sample = { "A,B,C": 4, "B,C,A": 3, "C,B,A": 2, "A,C,B": 2 };
const totalSum = Object.values(sample).reduce(
(previousValue, currentValue) => previousValue + currentValue,
0
);
const stretchWin = Math.round(totalSum / 2);
const winner = Object.entries(sample)
.filter(([key, value]) => {
const isFirstLetterA = key.startsWith("A");
return isFirstLetterA || value >= stretchWin;
})
.map(([key, value]) => value)
.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
console.log(winner);
I am taking input from a textbox :
1 ziro = 8.60
2 passighat = 7.70
3 bomdila = 5.30
4 sankalan = 1.20
5 shipgyar = 1.20
6 yuksom = 0.40
7 beki_mathungari = 125.20
8 hazuah = 36.40
9 melabazar/matunga = 13.20
js code:
var summary_table_content = $('#textbox').val();
var array1 = summary_table_content.split('\n');
var myarray = [];
for (i = 0; i < array1.length; i++) {
var line = array1[i];
var words = line.split(' ');
var word2 = words[1];
var word4 = words[3];
myarray[word2] = word4;
}
Now I want to arrange myarray in descending order by values and print it like this:
beki_mathungari 125.20, hazuah 36.40, melabazar/matunga 13.20, ziro 8.60, passighat 7.70, bomdila 5.30, sankalan 1.20, shipgyar 1.20, yuksom 0.40
I tried a solution from internet, it is working on integers but not with real numbers:
var tuples = [];
for (var key in myarray) tuples.push([key, myarray[key]]);
tuples.sort(function(a, b) {
a = a[1];
b = b[1];
return a < b ? -1 : (a > b ? 1 : 0);
});
for (var i = 0; i < tuples.length; i++) {
var key = tuples[i][0];
var value = tuples[i][1];
document.getElementById('id1').innerHTML += key + " " + value + ", ";
}
In your code, myarray isnt actually an array, it is an object. If you make this change, ordering by the values and joining the key/values is as easy as:
var myarray = {};
myarray["ziro"] = 8.60;
myarray["passighat"] = 7.70;
myarray["bomdila"] = 5.30;
myarray["sankalan"] = 1.20;
myarray["shipgyar"] = 1.20;
myarray["yuksom"] = 0.40;
myarray["beki_mathungari"] = 125.20;
myarray["hazuah"] = 36.40;
myarray["melabazar/matunga"] = 13.20;
var result = Object.entries(myarray)
.sort( (a,b) => b[1] - a[1])
.map( ([key,value]) => `${key} ${value.toFixed(2)}`)
.join(", ");
console.log(result)
Edit after your update. You've changed the question to be about converting string to a number. This step just needs to be done using parseFloatand then the above solution still works:
var input = `1 ziro = 8.60
2 passighat = 7.70
3 bomdila = 5.30
4 sankalan = 1.20
5 shipgyar = 1.20
6 yuksom = 0.40
7 beki_mathungari = 125.20
8 hazuah = 36.40
9 melabazar/matunga = 13.20`;
var result = input.split("\n")
.map(x => {
[word0,word1,word2,word3] = x.split(" ");
return [word1,parseFloat(word3)]
})
.sort( (a,b) => b[1] - a[1])
.map( ([key,value]) => `${key} ${value.toFixed(2)}`)
.join(", ");
console.log(result)
The data coming from your text box are strings, even the numbers. And if you sort the strings "125.20" and "13.20", they are sorted alphabetically, so "125.20" comes first because 2 comes before 3.
What you need to do is convert the numbers to actual numbers before storing them in the array, using the parseFloat function:
var word4 = parseFloat(words[3]);
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total)=>(num==0 ? total : total+num), 0)
console.log(sum(arr, 0))
Please check how can I make it work. Did some mistake but don't know what exactly. Output is a function instead of a result.
This is awkward to do in .reduce because it goes through the entire array. If we do a naive implementation you can see the problem:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=>(num==x ? total : total+x), 0)
console.log(sum(arr, 0))
We now make the check correctly - num==x will return true when x is zero (the value of num). However, the result is wrong because this only returns true once but any other iteration it's still true. And here is the same thing with more logging that describes each step of the process:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=> {
const boolCheck = num==x;
const result = boolCheck ? total : total+x;
console.log(
`total: ${total}
num: ${num}
x: ${x}
boolCheck: ${boolCheck}
result: ${result}`);
return result;
}, 0)
console.log(sum(arr, 0))
So, you need to add some flag that persists between iterations, so it doesn't get lost.
One option is to have an external flag that you change within the reduce callback:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
let finished = false;
return arr.reduce((total, x) => {
if(x === num)
finished = true;
return finished ? total : total+x;
}, 0)
}
console.log(sum(arr, 0))
Alternatively, you can have that flag internal to the reduce callback and pass it around between calls. It works the same way in the end but makes the callback function pure. At the cost of some unorthodox construct:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
return arr.reduce(({total, finished}, x) => {
if(x === num)
finished = true;
total = finished ? total : total+x;
return {total, finished};
}, {total: 0, finished: false})
.total
}
console.log(sum(arr, 0))
If you want to use reduce but you're OK with using other methods, then you can use Array#indexOf to find the first instance of a value and Array#slice the array that contains any value up to the target value:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
const endIndex = arr.indexOf(num);
return arr.slice(0, endIndex)
.reduce((total, x)=> total+x, 0)
}
console.log(sum(arr, 0))
Or in as one chained expression:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr
.slice(0, arr.indexOf(num))
.reduce((total, x)=> total+x, 0);
console.log(sum(arr, 0))
Other libraries may have a takeUntil or takeWhile operation which is even closer to what you want - it gets you an array from the beginning up to a given value or condition. You can then reduce the result of that.
Here is an example of this using Lodash#takeWhile
By using chaining here, Lodash will do lazy evaluation, so it will only go through the array once, instead of scanning once to find the end index and going through the array again to sum it.
const arr = [5,6,0,7,8];
const sum = (arr,num) => _(arr)
.takeWhile(x => x !== num)
.reduce((total, x)=>total+x, 0)
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
As a note, if you are using Lodash, then you may as well use _.sum(). I didn't above just to illustrate how a generic takeUntil/takeWhile looks.
const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => _(arr)
.takeWhile(x => x !== num)
.sum()
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Since you need to stop summing values part way through the array, this might be most simply implemented using a for loop:
const arr = [5, 6, 0, 7, 8];
const num = 0;
let sum = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] == num) break;
sum += arr[i];
}
console.log(sum);
If you want to use reduce, you need to keep a flag that says whether you have seen the num value so you can stop adding values from the array:
const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => {
let seen = false;
return arr.reduce((c, v) => {
if (seen || v == num) {
seen = true;
return c;
}
return c + v;
}, 0);
}
console.log(sum(arr, 0));
console.log(sum(arr, 8));
call it as follows:
console.log(sum(arr, 0)());
You need parenthesis to execute the function ()
sum(arr, 0)
Without parenthesis you store a reference to the function in the variable
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
How do I get 5 elements from an array without repetition?
I'm trying to create an online version of story dice using Tumult Hype. All that I need to do is choose 5 image names from an array without repetition. But I just can't get it to work.
I've tried borrowing code from other stackoverflow answers and I can't get them working.
The code below is currently working but gives me repeats. How do I tinker with it to eliminate the repeats?
(You can see it in action here: https://davebirss.com/storydice/)
I know that it's probably verbose and inelegant. I only speak pidgin javascript, I'm afraid. And what I'm trying to do is currently beyond my ability.
Thank you so much in advance for your help.
var diceRoll = ['${resourcesFolderName}/story_dice1.png',
'${resourcesFolderName}/story_dice2.png',
'${resourcesFolderName}/story_dice3.png',
...,
'${resourcesFolderName}/story_dice51.png']
function choose(n, arr) {
while (arr.length > n) {
var del = Math.floor(Math.random() * arr.length);
arr = arr.filter(function(item, i) {
return i !== del;
});
}
return arr;}
var result1 = [choose(1, diceRoll)];
var result2 = [choose(1, diceRoll)];
var result3 = [choose(1, diceRoll)];
var result4 = [choose(1, diceRoll)];
var result5 = [choose(1, diceRoll)];
hypeDocument.getElementById("dice1").innerHTML = "<img src='"+result1+" 'height='125' width='125'>";
hypeDocument.getElementById("dice2").innerHTML = "<img src='"+result2+" 'height='125' width='125'>";
hypeDocument.getElementById("dice3").innerHTML = "<img src='"+result3+" 'height='125' width='125'>";
hypeDocument.getElementById("dice4").innerHTML = "<img src='"+result4+" 'height='125' width='125'>";
hypeDocument.getElementById("dice5").innerHTML = "<img src='"+result5+" 'height='125' width='125'>";
Update
Thank you all for your help. I'm sure all the answers were great but the snippet from U25lYWt5IEJhc3RhcmQg is the code that I managed to successfully incorporate. For the record, this is how I did it:
const rollTheDice = (arr, n) => {
const randomN = [];
while(randomN.length < n){
const randomIndex = Math.floor(Math.random()*arr.length);
randomN.push(arr[randomIndex]);
arr.splice(randomIndex, 1);
}
return randomN;}
var result1 = (rollTheDice(images,1));
var result2 = (rollTheDice(images,1));
var result3 = (rollTheDice(images,1));
var result4 = (rollTheDice(images,1));
var result5 = (rollTheDice(images,1));
I've been repeatedly reloading the page and haven't seen any repeats yet. Perfect!
You could take an array of indices and check if the index exist, then get a new index or push this index.
var length = 51, // your count of items
indices = [], // the result set with indices
count = 5, // the amount of wanted indices
random; // guess what?
while (indices.length < count) { // check length
random = Math.floor(Math.random() * length); // get random value
if (indices.includes(random)) continue; // continue if already selected
indices.push(random); // if not take it
}
console.log(indices);
I guess, the trickiest part here is not to waste the performance, limiting possible options to those, not previously chosen:
const images = ['a','b','c','d','e','f'];
const rollTheDice = (arr, n) => {
const randomN = [];
while(randomN.length < n){
const randomIndex = Math.floor(Math.random()*arr.length);
randomN.push(arr[randomIndex]);
arr.splice(randomIndex, 1);
}
return randomN;
}
console.log(rollTheDice(images, 5));
Make a copy of diceRoll array (diceRollCopy).
Use the new array(diceRollCopy) as argument of choose method.
Whenever you get a
result using choose method remove that result from the Copy array
(diceRollCopy).
You would need to reset the diceRollCopy to diceRoll
after each set of results have been accessed.
Copy it, then shuffle the copy, and remove the first element from the array each time:
const copy = [...diceRoll].sort(e => 0.5 - Math.random());
And in your choosing function:
const chosen = copy.shift();
You want a random permutation which all elements is uniq and from one dataset, here is my implementation:
var array = [1, 2, 3, 4, 5, 6];
/**
* uniqGet
* #param {*} array source array
* #param {*} num how many elements to get
*/
function uniqGet(array, num) {
if (array.length < num) {
throw new Error("num should less than options");
}
let res = [];
while (num > 0) {
let index = Math.floor(Math.random() * array.length);
res.push(array[index]);
array.splice(index, 1);
num--;
}
return res;
}
let result = uniqGet(array, 3); // [x, y, z]