This problem has been bugging me for a while now and I can't seem to find an answer in web.
Is it possible to use Array reduce method starting from a certain index?
simple example
var studentGrades = ["John Doe", "Some School", 6, 7, 8, 7, 9, 9];
If I need to loop over only integers in studentGrades, I can do that with a simple for loop
for(var i = 2; i < studentGrades.length; i++) {
// do stuff here ...
}
But let's say I would need get an average grade which is sum of all integers divided by integers count. If Array contained only integers, then there would be no problem using reduce.
var onlyIntegersArr = [5,2,3,4];
var averageGrade = onlyIntegersArr.reduce(function(a,b){
return a + b;
}) / onlyIntegersArr.length;
However if I know that for whatever reasons I need to skip the first two Array elements and start from index Array[2].
So for example I would apply reduce to studentGrades, but only starting from index studentGrades[2].
Is that possible with reduce?
Thank you for solutions, I like slice approach, but I prefer not using a new method in this case.
e.g.
var average = studentGrades.reduce(function(a,b,i){
return i >= 2 ? a+b : 0;
}) / (studentGrades.length - 2);
reduce's 3rd argument is an index, here is the fiddle
var averageGrade = onlyIntegersArr.reduce(function (a, b, c) {
if (c >= 2) {
return a + b;
} else {
return 0;
}
});
if array has more non-numeric items after second index then check this fiddle
var studentGrades = ["John Doe", "Some School", 6, 7, 8, 7, 9, 9, "Some School"];
var averageGrade = studentGrades.reduce(function (a, b, c) {
if (c >= 2 && !isNaN(b)) {
return a + b;
} else if (c >= 2) {
return a + 0;
} else {
return 0;
}
})
alert(averageGrade);
If you know for a fact that you want to skip the first n elements, you can use Array#slice
Using ES2015 Arrow Function
var sum = array.slice(n).reduce((a, b) => a + b);
var studentGrades = ["John Doe", "Some School", 6, 7, 8, 7, 9, 9];
var sum = studentGrades.slice(2).reduce((a, b) => a + b);
document.body.innerHTML = 'SUM is = ' + sum;
In ES5, the same code can be written using anonymous function.
var sum = array.slice(n).reduce(function(a, b) {
return a + b;
});
var studentGrades = ["John Doe", "Some School", 6, 7, 8, 7, 9, 9];
var sum = studentGrades.slice(2).reduce(function(a, b) {
return a + b;
});
document.body.innerHTML = 'SUM is = ' + sum;
for the case you mentioned, of only adding up numeric values, regardless of where in the array they are - you could do something like
var sum = array.reduce(function(result, v) {
return result + (parseFloat(v) || 0);
}, 0);
var studentGrades = ["John Doe", "Some School", 6, 7, 8, 7, 9, 9];
var sum = studentGrades.reduce(function(result, v) {
return result + (parseFloat(v) || 0);
}, 0);
document.body.innerHTML = 'SUM is = ' + sum;
If you're sure you always need only index 2 onwards, then this is sufficient
var onlyIntegersArr = studentGrades.slice(2);
var averageGrade = onlyIntegersArr.reduce(function(a,b){
return a + b;
}) / onlyIntegersArr.length;
If, however, you want to get all integers, then you need to filter the array
var onlyIntegersArr = studentGrades.filter(function(val) {
return (val === parseInt(val, 10));
});
var averageGrade = onlyIntegersArr.reduce(function(a,b){
return a + b;
}) / onlyIntegersArr.length;
Why do you overcomplicate things? Why not:
function avg(arr)
{
var sum = 0;
var l = 0;
for(var i = 0; i < arr.length; i++)
{
if(isNaN(1*arr[i])) continue;
sum += arr[i];
l++;
}
return sum/l;
}
Maybe you need to think about keeping the data in object, where all the grades are in a separate array. And other data be in properties. You can serialize it from the array you have, and then work with the object.
Related
I am keeping track of each round of hands/results dealt in a blackjack game. I need to output each "round" as a single line. There are 'x' number of rounds. I am storing the results for each round in a big array. My problem is I have 2 arrays within my big array and 2 numeric. I need to output each of the 4 'objects' per line, as round results. I cannot figure out how to 'parse' the non-numeric array objects mixed with the numeric objects. Should I just create two arrays, one object and one numeric and output them separately?
Output goal is 1st line:
3,2,9,6;10,2,6;3,10
and so on for each round, one line.
<script>
var ph = [3,2,9,6]; var dh = [10,2,6]; var seq = 3, var bet = 10; var result;
allhands.push(ph);allhands.push(dh);allhands.push(seq);allhands.push(bet)
//allhands looks like this:[[3,2,9,6], [10,2,6], 3,10]
function showallhands(){
for (e = 0;e<allhands.length;e++){
for (f = 0; allhands[e][f];f ++){
result += allhands[e] + allhands[e][f]+";"+"<br >" ;
}
}
return result;
}
</script>
result = showallhands();
document.getElementById("ALLhands").innerHTML = result;
One approach is to create a game object then store the hands in it:
function Game() {
this.ph = [3, 2, 9, 6];
this.dh = [10, 2, 6];
this.seq = 3;
this.bet = 10;
this.getHands = function () {
return this.ph + ';' + this.dh + ';' + this.seq + ';' + this.bet + ';<br>';
};
};
var games = [];
games.push(new Game());
games.push(new Game());
games.push(new Game());
var result = "";
function showallhands() {
for (e = 0; e < games.length; e++) {
result += games[e].getHands();
}
return result;
}
result = showallhands();
document.getElementById("ALLhands").innerHTML = result;
If you want to keep the array and not use OOP, I think this would do the trick:
var arr = [
[3, 2, 9, 6],
[10, 2, 6], 3, 10, "hello"
];
arr.forEach(function each(item) {
if (Array.isArray(item)) {
item.forEach(each);
} else {
console.log(item);
}
});
Say I have a list [1,2,3,4,5,6,7]
and I would like to find the closest sum of numbers to a given number. Sorry for the crappy explanation but here's an example:
Say I have a list [1,2,3,4,5,6,7] I want to find the closest numbers to, say, 10.
Then the method should return 6 and 4 or 7 and 3 because its the closest he can get to 10. So 5 + 4 would be wrong because thats 9 and he can make a 10.
another example : you want the closest to 14 , so then he should return 7 and 6
If you got any questions plz ask because its difficult to explain what I want :P
Functions for combine, locationOf, are taken from different answers, written by different authors.
printClosest([0.5,2,4] , 5);
printClosest([1, 2, 3, 4, 5, 6, 7], 28);
printClosest([1, 2, 3, 4, 5, 6, 7], 10.9);
printClosest([1, 2, 3, 4, 5, 6, 7], 10, 2);
printClosest([1, 2, 3, 4, 5, 6, 7], 10, 3);
printClosest([1, 2, 3, 4, 5, 6, 7], 14, 2);
function printClosest(array, value, limit) {
var checkLength = function(array) {
return array.length === limit;
};
var combinations = combine(array); //get all combinations
combinations = limit ? combinations.filter(checkLength) : combinations;//limit length if required
var sum = combinations.map(function(c) { //create an array with sum of combinations
return c.reduce(function(p, c) {
return p + c;
}, 0)
});
var sumSorted = sum.slice(0).sort(function(a, b) {//sort sum array
return a - b;
});
index = locationOf(value, sumSorted);//find where the value fits in
//index = (Math.abs(value - sum[index]) <= Math.abs(value - sum[index + 1])) ? index : index + 1;
index = index >= sum.length ? sum.length - 1 : index;
index = sum.indexOf(sumSorted[index]);//get the respective combination
console.log(sum, combinations, index);
document.getElementById("result").innerHTML += "value : " + value + " combi: " + combinations[index].toString() + " (limit : " + (limit || "none") + ")<br>";
}
function combine(a) {
var fn = function(n, src, got, all) {
if (n == 0) {
if (got.length > 0) {
all[all.length] = got;
}
return;
}
for (var j = 0; j < src.length; j++) {
fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all);
}
return;
}
var all = [];
for (var i = 0; i < a.length; i++) {
fn(i, a, [], all);
}
all.push(a);
return all;
}
function locationOf(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = parseInt(start + (end - start) / 2, 10);
if (end - start <= 1 || array[pivot] === element) return pivot;
if (array[pivot] < element) {
return locationOf(element, array, pivot, end);
} else {
return locationOf(element, array, start, pivot);
}
}
<pre id="result"><pre>
var data= [1, 2, 3,4,5,6,7];
var closest = 14;
for (var x = 0; x < data.length; x++) {
for (var y = x+1; y < data.length; y++) {
if(data[x] + data[y] == closet){
alert(data[x].toString() + " " + data[y].toString());
}
}
}
From what I understood from your question, I made this snippet. I assumed you did not wanted to have the same digit twice (e.g 14 => 7 + 7).
It is working with your examples.
var arr = [1, 2, 3, 4, 5, 6, 7];
var a = 0, b = 0;
var nb = 14;
for(var i in arr) {
for(var j in arr) {
if(i != j) {
var tmp = arr[i] + arr[j];
if(tmp <= nb && tmp > a + b) {
a = arr[i];
b = arr[j];
}
}
}
}
document.write("Closest to " + nb + " => " + a + " + " + b);
I have a little bit long winded solution to the problem just so it is easier to see what is done.
The main benefits with solution below:
The second loop will not start from beginning of the array again. What I mean that instead of having loop_boundary for second loop as 0 as you normally would, here it starts from next index. This helps if your numbers array is long. However, if it as short as in example, the impact in performance is minimal. Decreasing first loop's boundary by one will prevent errors from happening.
Works even when the wanted number is 1 or negative numbers.
Fiddle:
JSFiddle
The code:
var numbers = [1,2,3,4,5,6,7];
var wanted_number = 1;
var closest_range, closest1, closest2 = null;
var loop1_boundary = numbers.length-1;
for(var i=0; i<loop1_boundary; i++) {
var start_index = i+1;
var loop2_boundary = numbers.length;
for(var k=start_index; k<loop2_boundary; k++) {
var number1 = parseInt(numbers[i]);
var number2 = parseInt(numbers[k]);
var sum = number1 + number2;
var range = wanted_number - sum;
document.write( number1+' + '+number2 +' < '+closest_range+'<br/>' );
if(Math.abs(range) < Math.abs(closest_range) || closest_range == null ) {
closest_range = range;
closest1 = number1;
closest2 = number2;
}
}
if(range==0){
break;
}
}
document.write( 'closest to given number was '+closest1+' and '+closest2+'. The range from wanted number is '+closest_range );
This proposal generates all possible combinations, collects them in an object which takes the sum as key and filters then the closest sum to the given value.
function getSum(array, sum) {
function add(a, b) { return a + b; }
function c(left, right) {
var s = right.reduce(add, 0);
if (s > sum) {
return;
}
if (!result.length || s === result[0].reduce(add, 0)) {
result.push(right);
} else if (s > result[0].reduce(add, 0)) {
result = [right];
}
left.forEach(function (a, i) {
var x = left.slice();
x.splice(i);
c(left.slice(0, i), [a].concat(right));
});
}
var result = [];
c(array, [], 0);
return result;
}
function print(o) {
document.write('<pre>' + JSON.stringify(o, 0, 4) + '</pre>');
}
print(getSum([1, 2, 3, 4, 5, 6, 7], 10));
print(getSum([1, 2, 3, 4, 5, 6, 7], 14));
print(getSum([1, 2, 3, 4, 5, 6, 7], 19));
i'm looking for this since yesterday, already searched a lot but didn't find any answer for exactly what I need. If you find any, just tell me, i'll appreciate and close this question :)
What I want is:
-> If there are an even number of digits, double every other digit starting with the first
-> If there are an odd number of digits, double every other digit starting with the second.
This is what I did so far:
function validate(n){
var num = n.toString(); // THIS COULD BE AN ARRAY OR WHATEVER
if (eval(num%2==0)) { // IF HAS EVEN NUMBER OF DIGITS
for (var i=0; i<num.length; i++) {
if (num.charAt(i) === num.charAt(0)) {
eval(num.charAt(i)*=2);
}
}
console.log(num);
} else { // IF HAS ODD NUMBER OF DIGITS
for (var i=0; i<num.length; i++) {
if (num.charAt(i) === num.charAt(1)) {
eval(num.charAt(i)*=2);
}
}
console.log(num);
}
}
validate(1234516178);
Examples:
1714 => [1*, 7, 1*, 4] => [2, 7, 2, 4]
891 => [8, 9*, 1] => [8, 18, 1]
Hope I was clear. Can someone help on this? Appreciate!
Maybe this works for you. It utilizes an array with the values and iterates over the values to change.
function validate(n) {
var array = n.toString().split('').map(Number),
number = n % 2 ? array[1] : array[0];
return array.map(function (a) {
return a === number ? 2 * a : a;
});
}
function print(o) {
document.write('<pre>' + JSON.stringify(o, 0, 4) + '</pre>');
}
print(validate(1234516178));
print(validate(1714)); // => [1*, 7, 1*, 4] => [2, 7, 2, 4]
print(validate(891)); // => [8, 9*, 1] => [8, 18, 1]
You could try something like this:
function validate(n) {
n = n.toString();
var isEven = (parseInt(n, 10) % 2 === 0);
var digits = n.split("");
// Strings to numbers
digits.forEach(function(digit, index) {
digits[index] = parseInt(digit, 10);
});
if(isEven) {
var firstDigit = digits[0];
digits.forEach(function(digit, index) {
console.log(digit);
if(digit === firstDigit) {
digits[index] *= 2;
}
});
} else {
var secondDigit = digits[1];
digits.forEach(function(digit, index) {
if(digit === secondDigit) {
digits[index] *= 2;
}
});
}
return JSON.stringify(digits);
}
This can be of course be made better with including more conditions and leaving just a single forEach
You dont need strings to do that
function validate(s){
var a = parseInt(s);
var b = 0;
var odd = (a+'').length %2//true;
//var summ = 0;
while(a>=1){
var b = Math.floor(a%10);
var c = odd?b: b*2//<10? b*2 : b*2-9;
//summ += c;
odd = !odd;
a = a/10;
//output
$('input').after('+'+c)
}
//output
$('input').after('<br>')//.after(summ).after('<br> summ: ')
//return summ%10
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button onclick="validate($('input').val())">validate</button>
<input>
I have an array e.g.
var arr = [2,7,3,8,9,4,9,2,8,7,9,7,3,2,4,5,7,8,2,7,6,1,8];
I want that (I think for-loop is best for this to loop over this) a for-loop loops over the whole array and gets 5 items near eachother in the array and runs a function with those 5 items to calculate an average of them. This has of course to repeat till there are no parts of 5 available. The array above has 23 values. So when I should run a code on it, it can loop 4 times on it, cos one more time can't cos it has 3/5 values.
I thought about doing:
for (var i = 0; i < arr.length; i++) {
doThisFunction(i, i+1, i+2, i+3, i+4 );
}
but that shouldn't be efficient I believe... any help?
You're on to something, the easy way to do it is
var arr = [2,7,3,8,9,4,9,2,8,7,9,7,3,2,4,5,7,8,2,7,6,1,8];
var result = [];
for (var i=0; (i+5)<arr.length; i=i+5) {
var average = (arr[i] + arr[i+1] + arr[i+2] + arr[i+3] + arr[i+4]) / 5;
result.push(average);
}
document.body.innerHTML = '<pre>' + JSON.stringify(result, null, 4) + '</pre>';
The somewhat fancier way to do the same thing
var result = arr.map(function(x,i) {
return i%5===0 ? arr.slice(i, i+5).reduce(function(a,b) {return a+b}) / 5 : NaN;
}).filter(isFinite);
Use array.slice:
for (var i = 0; i < Math.floor(arr.length/5); i++) {
f(arr.slice(i*5, i*5+5))
}
The following uses reduce and a slice to sum up a range of values from the array.
function averageRange(arr, start, end) {
return (function(range) {
return range.reduce(
function(total, val) {
return total + val;
}, 0) / range.length;
}([].slice.apply(arr, [].slice.call(arguments, 1))))
}
function averageEveryN(arr, n) {
return arr.map(function(_, index, arr) {
return index % n === 0 ? averageRange(arr, index, index + count) : NaN;
}).filter(isFinite).slice(0, Math.floor(arr.length / n));
}
function println(text) {
document.getElementsByTagName('body')[0].innerHTML += text + '<br />';
}
var arr = [2, 7, 3, 8, 9, 4, 9, 2, 8, 7, 9, 7, 3, 2, 4, 5, 7, 8, 2, 7, 6, 1, 8];
var count = 5;
averageEveryN(arr, count).map(function(value, index) {
println((index + 1) + '.) ' + value.toFixed(4));
});
Output
1.) 5.8000
2.) 6.0000
3.) 5.0000
4.) 5.8000
I have several array to deal with. I need to extract the most duplicate value from each array.
From [3, 7, 7, 7], I need to find the value 7. Each array size is 4. For now, I don't have to think about when the most duplicate values are more than one such as [3, 7, 7, 7]. All the values are a number.
I looked around the web. I found several ways to make an array to become uniq(). But I haven't found a way to get the duplicate value. I am using jQuery, but raw JavaScript is fine for this task.
Not perfect in terms of efficiency, but does the job:
var nums = [3, 7, 7, 7];
var freqs = {};
var max_index;
var max_value = -1/0; // Negative infinity.
$.each(nums, function(i, v) {
if (freqs[v] != undefined) {
freqs[v]++;
} else {
freqs[v] = 1;
}
});
$.each(freqs, function(num, freq) {
if (freq > max_value) {
max_value = freq;
max_index = num;
}
});
if (max_index != undefined) {
alert("Most common element is " + max_index + " with " + max_value + " repetition(s).");
}
Here's a simpler and faster version using only JavaScript:
var arr = [3, 7, 7, 7, 10, 10, 8, 5, 5, 5, 5, 20, 20, 1];
var counts = {}, max = 0, res;
for (var v in arr) {
counts[arr[v]] = (counts[arr[v]] || 0) + 1;
if (counts[arr[v]] > max) {
max = counts[arr[v]];
res = arr[v];
}
}
alert(res + " occurs " + counts[res] + " times");
Note that this is a much more efficient since you're looping over the data once, if you're sorting very large arrays this will start to matter.
Here's a quick example using javascript:
function mostFrequent(arr) {
var uniqs = {};
for(var i = 0; i < arr.length; i++) {
uniqs[arr[i]] = (uniqs[arr[i]] || 0) + 1;
}
var max = { val: arr[0], count: 1 };
for(var u in uniqs) {
if(max.count < uniqs[u]) { max = { val: u, count: uniqs[u] }; }
}
return max.val;
}
A quick note on algorithmic complexity -- because you have to look at each value in the array at least once, you cannot do better than O(n). This is assuming that you have no knowledge of the contents of the array. If you do have prior knowledge (e.g. the array is sorted and only contains 1s and 0s), then you can devise an algorithm with a run time that is some fraction of n; though technically speaking, it's complexity is still O(n).
Array.prototype.mostFreq=function(){
var what, a= this.concat(), ax, freq,
count, max=0, limit= a.length/2;
while(a.length){
what= a.shift();
count=1;
while((ax= a.indexOf(what))!= -1){
a.splice(ax,1); // remove counted items
++count;
}
// if any item has more than half the array, quit counting
if(count> limit) return what;
if(count> max){
freq= what;
max= count;
}
}
return freq;
}
var a=[1,1,2,5,4,2,7,7,1,1,1,3,7,7,3,4,3,7,3,5,6,2,3,1,1,7,7,2,4,3,6,7,6,6]
alert(a.mostFreq())
Another solution can be based on Array.reduce():
var arr = [1,1,2,5,4,2,10,10,1,1,1,3,10,10,3,4,3,10,3,5,6,2,3,1,1,10,10,2,4,3,6,10,6,6];
var result = arr.reduce(function(acc, e) {
acc[e] = (acc[e] || 0) + 1;
if (acc[e] > acc.mostFreq.freq) {
acc.mostFreq.value = e;
acc.mostFreq.freq = acc[e];
}
return acc;
}, {"mostFreq": {"value": 0, "freq": 0}}).mostFreq;
console.log('The most duplicated elements is: ' + JSON.stringify(result));