Simple function returning 1 if the Mean = Mode, or 0 if not
up vote
5
down vote
favorite
I'm trying to get better at javascript and have built a simple function that returns 1 if the mode is equal to the mean, and 0 if not. You can enter an array of numbers into the function:
// tests
meanMode([1,2,3]); - returns 0
meanMode([4,4,4,6,2]); - returns 1
meanMode([4,4,4,6,6,6,2]); - returns 0
To clarify, the mean is the middle number in the array, and mode most occurring number.
When I was writing this, I wondered whether I create too many variables and overcomplicate things. The problems I thought I might be making are:
- There are too many variables
- It's confusing and over complex
- It can be hard to retrace my logic and know what the code does and why it's there
I have a few questions that I hope better coders than myself can answer, as I'm relatively new to javascript:
- Was using a counts variable necessary?
- Are all variable keywords (const & let) optimal?
- Is it wise to include comments to describe what the code does?
- Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
Before writing this I wrote a brief logic plan as follows:
Mode:
- var uniqueVals with unique vals (set)
- for each unique value (uniqueVals.map), count no of occurrences in arr (filter.length) & store as an array
- if all values are identical, return 0, otherwise get the most frequent & store in variable (there may be cases with no mode)
Mean:
- sum all numbers
- divide by no of numbers
And:
- if unique values length is same as arr length, return 0
- if mean = mode, return 1
When people write programming, is it usually best to have a detailed plan to follow? I wonder whether there are any best practices that I could follow to improve my coding ability.
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr));
let counts = uniqueVals.map(function (c, i, a) {
return arr.filter(function (c2) {
return c2 == c
}).length
}); // [3,1,1]
if (arr.every(sameValues)) {
return 0;
} else {
var mode;
// if highest number in counts appears just once, then there is a mode
if ((counts.filter(function (x) { return x == (Math.max(...counts)) }).length) == 1) {
mode = uniqueVals[counts.indexOf(Math.max(...counts))]; // scope issue?
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { return a + c })) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
javascript beginner statistics
add a comment |
up vote
5
down vote
favorite
I'm trying to get better at javascript and have built a simple function that returns 1 if the mode is equal to the mean, and 0 if not. You can enter an array of numbers into the function:
// tests
meanMode([1,2,3]); - returns 0
meanMode([4,4,4,6,2]); - returns 1
meanMode([4,4,4,6,6,6,2]); - returns 0
To clarify, the mean is the middle number in the array, and mode most occurring number.
When I was writing this, I wondered whether I create too many variables and overcomplicate things. The problems I thought I might be making are:
- There are too many variables
- It's confusing and over complex
- It can be hard to retrace my logic and know what the code does and why it's there
I have a few questions that I hope better coders than myself can answer, as I'm relatively new to javascript:
- Was using a counts variable necessary?
- Are all variable keywords (const & let) optimal?
- Is it wise to include comments to describe what the code does?
- Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
Before writing this I wrote a brief logic plan as follows:
Mode:
- var uniqueVals with unique vals (set)
- for each unique value (uniqueVals.map), count no of occurrences in arr (filter.length) & store as an array
- if all values are identical, return 0, otherwise get the most frequent & store in variable (there may be cases with no mode)
Mean:
- sum all numbers
- divide by no of numbers
And:
- if unique values length is same as arr length, return 0
- if mean = mode, return 1
When people write programming, is it usually best to have a detailed plan to follow? I wonder whether there are any best practices that I could follow to improve my coding ability.
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr));
let counts = uniqueVals.map(function (c, i, a) {
return arr.filter(function (c2) {
return c2 == c
}).length
}); // [3,1,1]
if (arr.every(sameValues)) {
return 0;
} else {
var mode;
// if highest number in counts appears just once, then there is a mode
if ((counts.filter(function (x) { return x == (Math.max(...counts)) }).length) == 1) {
mode = uniqueVals[counts.indexOf(Math.max(...counts))]; // scope issue?
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { return a + c })) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
javascript beginner statistics
add a comment |
up vote
5
down vote
favorite
up vote
5
down vote
favorite
I'm trying to get better at javascript and have built a simple function that returns 1 if the mode is equal to the mean, and 0 if not. You can enter an array of numbers into the function:
// tests
meanMode([1,2,3]); - returns 0
meanMode([4,4,4,6,2]); - returns 1
meanMode([4,4,4,6,6,6,2]); - returns 0
To clarify, the mean is the middle number in the array, and mode most occurring number.
When I was writing this, I wondered whether I create too many variables and overcomplicate things. The problems I thought I might be making are:
- There are too many variables
- It's confusing and over complex
- It can be hard to retrace my logic and know what the code does and why it's there
I have a few questions that I hope better coders than myself can answer, as I'm relatively new to javascript:
- Was using a counts variable necessary?
- Are all variable keywords (const & let) optimal?
- Is it wise to include comments to describe what the code does?
- Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
Before writing this I wrote a brief logic plan as follows:
Mode:
- var uniqueVals with unique vals (set)
- for each unique value (uniqueVals.map), count no of occurrences in arr (filter.length) & store as an array
- if all values are identical, return 0, otherwise get the most frequent & store in variable (there may be cases with no mode)
Mean:
- sum all numbers
- divide by no of numbers
And:
- if unique values length is same as arr length, return 0
- if mean = mode, return 1
When people write programming, is it usually best to have a detailed plan to follow? I wonder whether there are any best practices that I could follow to improve my coding ability.
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr));
let counts = uniqueVals.map(function (c, i, a) {
return arr.filter(function (c2) {
return c2 == c
}).length
}); // [3,1,1]
if (arr.every(sameValues)) {
return 0;
} else {
var mode;
// if highest number in counts appears just once, then there is a mode
if ((counts.filter(function (x) { return x == (Math.max(...counts)) }).length) == 1) {
mode = uniqueVals[counts.indexOf(Math.max(...counts))]; // scope issue?
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { return a + c })) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
javascript beginner statistics
I'm trying to get better at javascript and have built a simple function that returns 1 if the mode is equal to the mean, and 0 if not. You can enter an array of numbers into the function:
// tests
meanMode([1,2,3]); - returns 0
meanMode([4,4,4,6,2]); - returns 1
meanMode([4,4,4,6,6,6,2]); - returns 0
To clarify, the mean is the middle number in the array, and mode most occurring number.
When I was writing this, I wondered whether I create too many variables and overcomplicate things. The problems I thought I might be making are:
- There are too many variables
- It's confusing and over complex
- It can be hard to retrace my logic and know what the code does and why it's there
I have a few questions that I hope better coders than myself can answer, as I'm relatively new to javascript:
- Was using a counts variable necessary?
- Are all variable keywords (const & let) optimal?
- Is it wise to include comments to describe what the code does?
- Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
Before writing this I wrote a brief logic plan as follows:
Mode:
- var uniqueVals with unique vals (set)
- for each unique value (uniqueVals.map), count no of occurrences in arr (filter.length) & store as an array
- if all values are identical, return 0, otherwise get the most frequent & store in variable (there may be cases with no mode)
Mean:
- sum all numbers
- divide by no of numbers
And:
- if unique values length is same as arr length, return 0
- if mean = mode, return 1
When people write programming, is it usually best to have a detailed plan to follow? I wonder whether there are any best practices that I could follow to improve my coding ability.
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr));
let counts = uniqueVals.map(function (c, i, a) {
return arr.filter(function (c2) {
return c2 == c
}).length
}); // [3,1,1]
if (arr.every(sameValues)) {
return 0;
} else {
var mode;
// if highest number in counts appears just once, then there is a mode
if ((counts.filter(function (x) { return x == (Math.max(...counts)) }).length) == 1) {
mode = uniqueVals[counts.indexOf(Math.max(...counts))]; // scope issue?
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { return a + c })) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
javascript beginner statistics
javascript beginner statistics
edited Nov 24 at 11:47
kleinfreund
2,93932557
2,93932557
asked Nov 22 at 19:11
user8758206
1763
1763
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
up vote
3
down vote
accepted
First of all, I don’t think this is reasonable:
if unique values length is same as arr length, return 0
If your array consists of unique values only (e.g. [1, 2, 3, 4]
), its mode is not unique, rather there is multiple modes (in this case [1, 2, 3, 4]
). How you define what mode exactly means depends on your application so I’m going to ignore this for now.
Let’s look at your code. I’ll list some observations:
Function name. A name likemeanEqualsMode
would more clearly describe what the function does
Unused variables. There are a few unused variables. Line 3 uses three arguments for themap
function’s callback, but the callback is only ever called with one argument: The current array element. You also never usei
anda
.
Single-letter variable names. This style of naming problematic for a variety of reasons. Most importantly, it’s harder to reason about your own code. For example, in line 21, thereduce
function hasa
andc
as callback arguments.sum
andcount
would be better names.
Separation of concerns. YourmeanMode
function does multiple things. It calculates the mode and the mean. Instead, use separate functions to calculate the mode and mean separately. If your applications uses the check for equality of mode and mean a lot, this would be a third function calling the other two. I’ve done that in the updated code below.
Predictable results. You state thatmeanMode
should return whether the mode and the mean are equal; however, it actually returns aNumber
(1
or0
). A comparison function (e.g. a function containing the words is, equal, or similar) should always return a boolean value (true
orfalse
). This again makes it easier to reason about your code. Just readingmeanEqualsMode([2, 3, 5])
should tell you what the result of that function will be without the need to look at the actual implementation.
function meanEqualsMode(array) {
const mode = arrayMode(array);
const mean = arrayMean(array);
return mode === mean;
}
function arrayMode(array) {
const frequencies = new Map();
for (const value of array) {
const currentCount = frequencies.has(value) ? frequencies.get(value) : 0;
frequencies.set(value, currentCount + 1);
}
let highestCount = 0;
let mostOccurringValue;
for (const [value, count] of frequencies) {
if (count >= highestCount) {
highestCount = count;
mostOccurringValue = value;
}
}
return mostOccurringValue;
}
function arrayMean(array) {
return array.reduce((sum, value) => sum + value) / array.length;
}
Now, answering some of your questions:
Was using a counts variable necessary?
Yes and no. In order to calculate the mode of a list of numbers, you need to know the number of occurrences for each number. However, it is possible to determine the mode while counting the occurences; thus, allowing you to calculate the mode without explicitly storing the occurences. To keep the code simple, I opted for not combining these steps.
Are all variable keywords (const & let) optimal?
Probably not. You are using const
and let
. There is very, very little reason to also still use var
at all. Most variables are assigned only once and can be defined using const
. Some variables have to be declared using let
because they’re (re-)assigned later.
Is it wise to include comments to describe what the code does?
Yes. Take my code for example. Is there something you don’t understand? Then it could’ve maybe explained with a comment. You used a reduce
function in your code which I often find hard to read. You need to ask yourself: Is my code dealing with a complex problem that needs explaining or is it just written in a hardly readable fashion? A complex problem is best explained with comments. Code which is hard to read and thus hard to reason about is better fixed by making it more readable and obvious.
Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
No, it wasn’t. The mode of a list of numbers always exists although it might not be unique. Therefor its value does not logically depend on a condition.
VERY helpful, thank you. I'll note all of these useful points down. Also I like how you used a for of loop and a new Map to count the number of instances in each array - I'll definitely be copying this trick!
– user8758206
Nov 25 at 11:12
add a comment |
up vote
1
down vote
Too complex
As the issue of complexity has not come up and as the functions complexity is O(n2) for a problem that can be solved in O(n) where n is length of arr
, I must point out some of the flaws.
The code comments show all the iterations for a worst case argument [2,4,6,8,12,14,16,18,10,10]
the return will be 1
. (**
is exponential operator)
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr)); // << array iteration * 2
let counts = uniqueVals.map(function (c, i, a) { // << outer iteration
return arr.filter(function (c2) { // << inner iteration **2
return c2 == c
}).length
});
if (arr.every(sameValues)) { // << array iteration
return 0;
} else {
var mode;
if ((counts.filter(function (x) { // << outer iteration
return x == (Math.max(...counts)) // << inner iteration **2 to get max
}).length) == 1) {
// Next line has nested iteration **2 to get max again
mode = uniqueVals[counts.indexOf(Math.max(...counts))];
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { // << array iteration
return a + c
})) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
Not only are the 3 instances of iteration at O(n2) unneeded, you use Array.filter
to count!! :( ... Use Array.reduce
to count so you don't need to allocate memory each time you add 1.
There are also repeated iterations that compute the same value. Math.max(...counts)
requires one full iteration of counts
each time it is called and you call it in the worst case 2n times.
Even when you add these optimisations (Array.reduce
rather than Array.filter
and calculate max once) and gain about 50% performance you are still at O(n2).
In a single pass O(n)
Using a counts = new Map();
and a single iteration you can compute the sum
to get the mean (after iteration), count each items frequency (via the map), and as you count the frequency you can keep a record of the top two max counts and the current max mode
.
After the iteration compute the mean
from the sum
divide the arr.length
, check if mode
is equal to mean
and if it is check that the top two frequencies are not equal to return 1 (or true).
An example of O(n) solution tuned for performance. (has optimisation that give an advantage if there are many sequences of the same value)
function isMeanMode(arr) {
const counts = new Map();
var i, prevMaxVal, item, maxA = -1, maxB = -1, index = 0, max = 1, sum = 0;
for (i = 0; i < arr.length; i ++) { // Only one iteration O(n)
const val = arr[i];
const item = counts.get(val);
sum += val;
if (item) {
item.count++;
while (val === arr[i + 1]) { // for quick count of sequences
i++;
item.count++;
sum += val;
}
if (max <= item.count) {
max = item.count;
if (prevMaxVal !== val) { // tracks the top two counts
maxB = maxA; // if maxA and maxB are the same
maxA = max; // after iteration then 2 or more mode vals
} else { maxA = max }
index = i;
prevMaxVal = val;
}
} else { counts.set(val, {count : 1}) }
}
if (counts.size === arr.length || maxB !== -1 && maxA === maxB) { return 0 }
return arr[index] === sum / arr.length ? 1 : 0;
}
The above solution has a performance increase from 2 orders of magnitude + faster for large random arrays, to 3-4 times faster for very small arrays.
thank you - very interesting information
– user8758206
Nov 25 at 11:11
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
First of all, I don’t think this is reasonable:
if unique values length is same as arr length, return 0
If your array consists of unique values only (e.g. [1, 2, 3, 4]
), its mode is not unique, rather there is multiple modes (in this case [1, 2, 3, 4]
). How you define what mode exactly means depends on your application so I’m going to ignore this for now.
Let’s look at your code. I’ll list some observations:
Function name. A name likemeanEqualsMode
would more clearly describe what the function does
Unused variables. There are a few unused variables. Line 3 uses three arguments for themap
function’s callback, but the callback is only ever called with one argument: The current array element. You also never usei
anda
.
Single-letter variable names. This style of naming problematic for a variety of reasons. Most importantly, it’s harder to reason about your own code. For example, in line 21, thereduce
function hasa
andc
as callback arguments.sum
andcount
would be better names.
Separation of concerns. YourmeanMode
function does multiple things. It calculates the mode and the mean. Instead, use separate functions to calculate the mode and mean separately. If your applications uses the check for equality of mode and mean a lot, this would be a third function calling the other two. I’ve done that in the updated code below.
Predictable results. You state thatmeanMode
should return whether the mode and the mean are equal; however, it actually returns aNumber
(1
or0
). A comparison function (e.g. a function containing the words is, equal, or similar) should always return a boolean value (true
orfalse
). This again makes it easier to reason about your code. Just readingmeanEqualsMode([2, 3, 5])
should tell you what the result of that function will be without the need to look at the actual implementation.
function meanEqualsMode(array) {
const mode = arrayMode(array);
const mean = arrayMean(array);
return mode === mean;
}
function arrayMode(array) {
const frequencies = new Map();
for (const value of array) {
const currentCount = frequencies.has(value) ? frequencies.get(value) : 0;
frequencies.set(value, currentCount + 1);
}
let highestCount = 0;
let mostOccurringValue;
for (const [value, count] of frequencies) {
if (count >= highestCount) {
highestCount = count;
mostOccurringValue = value;
}
}
return mostOccurringValue;
}
function arrayMean(array) {
return array.reduce((sum, value) => sum + value) / array.length;
}
Now, answering some of your questions:
Was using a counts variable necessary?
Yes and no. In order to calculate the mode of a list of numbers, you need to know the number of occurrences for each number. However, it is possible to determine the mode while counting the occurences; thus, allowing you to calculate the mode without explicitly storing the occurences. To keep the code simple, I opted for not combining these steps.
Are all variable keywords (const & let) optimal?
Probably not. You are using const
and let
. There is very, very little reason to also still use var
at all. Most variables are assigned only once and can be defined using const
. Some variables have to be declared using let
because they’re (re-)assigned later.
Is it wise to include comments to describe what the code does?
Yes. Take my code for example. Is there something you don’t understand? Then it could’ve maybe explained with a comment. You used a reduce
function in your code which I often find hard to read. You need to ask yourself: Is my code dealing with a complex problem that needs explaining or is it just written in a hardly readable fashion? A complex problem is best explained with comments. Code which is hard to read and thus hard to reason about is better fixed by making it more readable and obvious.
Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
No, it wasn’t. The mode of a list of numbers always exists although it might not be unique. Therefor its value does not logically depend on a condition.
VERY helpful, thank you. I'll note all of these useful points down. Also I like how you used a for of loop and a new Map to count the number of instances in each array - I'll definitely be copying this trick!
– user8758206
Nov 25 at 11:12
add a comment |
up vote
3
down vote
accepted
First of all, I don’t think this is reasonable:
if unique values length is same as arr length, return 0
If your array consists of unique values only (e.g. [1, 2, 3, 4]
), its mode is not unique, rather there is multiple modes (in this case [1, 2, 3, 4]
). How you define what mode exactly means depends on your application so I’m going to ignore this for now.
Let’s look at your code. I’ll list some observations:
Function name. A name likemeanEqualsMode
would more clearly describe what the function does
Unused variables. There are a few unused variables. Line 3 uses three arguments for themap
function’s callback, but the callback is only ever called with one argument: The current array element. You also never usei
anda
.
Single-letter variable names. This style of naming problematic for a variety of reasons. Most importantly, it’s harder to reason about your own code. For example, in line 21, thereduce
function hasa
andc
as callback arguments.sum
andcount
would be better names.
Separation of concerns. YourmeanMode
function does multiple things. It calculates the mode and the mean. Instead, use separate functions to calculate the mode and mean separately. If your applications uses the check for equality of mode and mean a lot, this would be a third function calling the other two. I’ve done that in the updated code below.
Predictable results. You state thatmeanMode
should return whether the mode and the mean are equal; however, it actually returns aNumber
(1
or0
). A comparison function (e.g. a function containing the words is, equal, or similar) should always return a boolean value (true
orfalse
). This again makes it easier to reason about your code. Just readingmeanEqualsMode([2, 3, 5])
should tell you what the result of that function will be without the need to look at the actual implementation.
function meanEqualsMode(array) {
const mode = arrayMode(array);
const mean = arrayMean(array);
return mode === mean;
}
function arrayMode(array) {
const frequencies = new Map();
for (const value of array) {
const currentCount = frequencies.has(value) ? frequencies.get(value) : 0;
frequencies.set(value, currentCount + 1);
}
let highestCount = 0;
let mostOccurringValue;
for (const [value, count] of frequencies) {
if (count >= highestCount) {
highestCount = count;
mostOccurringValue = value;
}
}
return mostOccurringValue;
}
function arrayMean(array) {
return array.reduce((sum, value) => sum + value) / array.length;
}
Now, answering some of your questions:
Was using a counts variable necessary?
Yes and no. In order to calculate the mode of a list of numbers, you need to know the number of occurrences for each number. However, it is possible to determine the mode while counting the occurences; thus, allowing you to calculate the mode without explicitly storing the occurences. To keep the code simple, I opted for not combining these steps.
Are all variable keywords (const & let) optimal?
Probably not. You are using const
and let
. There is very, very little reason to also still use var
at all. Most variables are assigned only once and can be defined using const
. Some variables have to be declared using let
because they’re (re-)assigned later.
Is it wise to include comments to describe what the code does?
Yes. Take my code for example. Is there something you don’t understand? Then it could’ve maybe explained with a comment. You used a reduce
function in your code which I often find hard to read. You need to ask yourself: Is my code dealing with a complex problem that needs explaining or is it just written in a hardly readable fashion? A complex problem is best explained with comments. Code which is hard to read and thus hard to reason about is better fixed by making it more readable and obvious.
Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
No, it wasn’t. The mode of a list of numbers always exists although it might not be unique. Therefor its value does not logically depend on a condition.
VERY helpful, thank you. I'll note all of these useful points down. Also I like how you used a for of loop and a new Map to count the number of instances in each array - I'll definitely be copying this trick!
– user8758206
Nov 25 at 11:12
add a comment |
up vote
3
down vote
accepted
up vote
3
down vote
accepted
First of all, I don’t think this is reasonable:
if unique values length is same as arr length, return 0
If your array consists of unique values only (e.g. [1, 2, 3, 4]
), its mode is not unique, rather there is multiple modes (in this case [1, 2, 3, 4]
). How you define what mode exactly means depends on your application so I’m going to ignore this for now.
Let’s look at your code. I’ll list some observations:
Function name. A name likemeanEqualsMode
would more clearly describe what the function does
Unused variables. There are a few unused variables. Line 3 uses three arguments for themap
function’s callback, but the callback is only ever called with one argument: The current array element. You also never usei
anda
.
Single-letter variable names. This style of naming problematic for a variety of reasons. Most importantly, it’s harder to reason about your own code. For example, in line 21, thereduce
function hasa
andc
as callback arguments.sum
andcount
would be better names.
Separation of concerns. YourmeanMode
function does multiple things. It calculates the mode and the mean. Instead, use separate functions to calculate the mode and mean separately. If your applications uses the check for equality of mode and mean a lot, this would be a third function calling the other two. I’ve done that in the updated code below.
Predictable results. You state thatmeanMode
should return whether the mode and the mean are equal; however, it actually returns aNumber
(1
or0
). A comparison function (e.g. a function containing the words is, equal, or similar) should always return a boolean value (true
orfalse
). This again makes it easier to reason about your code. Just readingmeanEqualsMode([2, 3, 5])
should tell you what the result of that function will be without the need to look at the actual implementation.
function meanEqualsMode(array) {
const mode = arrayMode(array);
const mean = arrayMean(array);
return mode === mean;
}
function arrayMode(array) {
const frequencies = new Map();
for (const value of array) {
const currentCount = frequencies.has(value) ? frequencies.get(value) : 0;
frequencies.set(value, currentCount + 1);
}
let highestCount = 0;
let mostOccurringValue;
for (const [value, count] of frequencies) {
if (count >= highestCount) {
highestCount = count;
mostOccurringValue = value;
}
}
return mostOccurringValue;
}
function arrayMean(array) {
return array.reduce((sum, value) => sum + value) / array.length;
}
Now, answering some of your questions:
Was using a counts variable necessary?
Yes and no. In order to calculate the mode of a list of numbers, you need to know the number of occurrences for each number. However, it is possible to determine the mode while counting the occurences; thus, allowing you to calculate the mode without explicitly storing the occurences. To keep the code simple, I opted for not combining these steps.
Are all variable keywords (const & let) optimal?
Probably not. You are using const
and let
. There is very, very little reason to also still use var
at all. Most variables are assigned only once and can be defined using const
. Some variables have to be declared using let
because they’re (re-)assigned later.
Is it wise to include comments to describe what the code does?
Yes. Take my code for example. Is there something you don’t understand? Then it could’ve maybe explained with a comment. You used a reduce
function in your code which I often find hard to read. You need to ask yourself: Is my code dealing with a complex problem that needs explaining or is it just written in a hardly readable fashion? A complex problem is best explained with comments. Code which is hard to read and thus hard to reason about is better fixed by making it more readable and obvious.
Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
No, it wasn’t. The mode of a list of numbers always exists although it might not be unique. Therefor its value does not logically depend on a condition.
First of all, I don’t think this is reasonable:
if unique values length is same as arr length, return 0
If your array consists of unique values only (e.g. [1, 2, 3, 4]
), its mode is not unique, rather there is multiple modes (in this case [1, 2, 3, 4]
). How you define what mode exactly means depends on your application so I’m going to ignore this for now.
Let’s look at your code. I’ll list some observations:
Function name. A name likemeanEqualsMode
would more clearly describe what the function does
Unused variables. There are a few unused variables. Line 3 uses three arguments for themap
function’s callback, but the callback is only ever called with one argument: The current array element. You also never usei
anda
.
Single-letter variable names. This style of naming problematic for a variety of reasons. Most importantly, it’s harder to reason about your own code. For example, in line 21, thereduce
function hasa
andc
as callback arguments.sum
andcount
would be better names.
Separation of concerns. YourmeanMode
function does multiple things. It calculates the mode and the mean. Instead, use separate functions to calculate the mode and mean separately. If your applications uses the check for equality of mode and mean a lot, this would be a third function calling the other two. I’ve done that in the updated code below.
Predictable results. You state thatmeanMode
should return whether the mode and the mean are equal; however, it actually returns aNumber
(1
or0
). A comparison function (e.g. a function containing the words is, equal, or similar) should always return a boolean value (true
orfalse
). This again makes it easier to reason about your code. Just readingmeanEqualsMode([2, 3, 5])
should tell you what the result of that function will be without the need to look at the actual implementation.
function meanEqualsMode(array) {
const mode = arrayMode(array);
const mean = arrayMean(array);
return mode === mean;
}
function arrayMode(array) {
const frequencies = new Map();
for (const value of array) {
const currentCount = frequencies.has(value) ? frequencies.get(value) : 0;
frequencies.set(value, currentCount + 1);
}
let highestCount = 0;
let mostOccurringValue;
for (const [value, count] of frequencies) {
if (count >= highestCount) {
highestCount = count;
mostOccurringValue = value;
}
}
return mostOccurringValue;
}
function arrayMean(array) {
return array.reduce((sum, value) => sum + value) / array.length;
}
Now, answering some of your questions:
Was using a counts variable necessary?
Yes and no. In order to calculate the mode of a list of numbers, you need to know the number of occurrences for each number. However, it is possible to determine the mode while counting the occurences; thus, allowing you to calculate the mode without explicitly storing the occurences. To keep the code simple, I opted for not combining these steps.
Are all variable keywords (const & let) optimal?
Probably not. You are using const
and let
. There is very, very little reason to also still use var
at all. Most variables are assigned only once and can be defined using const
. Some variables have to be declared using let
because they’re (re-)assigned later.
Is it wise to include comments to describe what the code does?
Yes. Take my code for example. Is there something you don’t understand? Then it could’ve maybe explained with a comment. You used a reduce
function in your code which I often find hard to read. You need to ask yourself: Is my code dealing with a complex problem that needs explaining or is it just written in a hardly readable fashion? A complex problem is best explained with comments. Code which is hard to read and thus hard to reason about is better fixed by making it more readable and obvious.
Was declaring the mode variable before assigning its value the best way? Its value depends on a condition
No, it wasn’t. The mode of a list of numbers always exists although it might not be unique. Therefor its value does not logically depend on a condition.
edited Nov 24 at 12:57
answered Nov 24 at 12:39
kleinfreund
2,93932557
2,93932557
VERY helpful, thank you. I'll note all of these useful points down. Also I like how you used a for of loop and a new Map to count the number of instances in each array - I'll definitely be copying this trick!
– user8758206
Nov 25 at 11:12
add a comment |
VERY helpful, thank you. I'll note all of these useful points down. Also I like how you used a for of loop and a new Map to count the number of instances in each array - I'll definitely be copying this trick!
– user8758206
Nov 25 at 11:12
VERY helpful, thank you. I'll note all of these useful points down. Also I like how you used a for of loop and a new Map to count the number of instances in each array - I'll definitely be copying this trick!
– user8758206
Nov 25 at 11:12
VERY helpful, thank you. I'll note all of these useful points down. Also I like how you used a for of loop and a new Map to count the number of instances in each array - I'll definitely be copying this trick!
– user8758206
Nov 25 at 11:12
add a comment |
up vote
1
down vote
Too complex
As the issue of complexity has not come up and as the functions complexity is O(n2) for a problem that can be solved in O(n) where n is length of arr
, I must point out some of the flaws.
The code comments show all the iterations for a worst case argument [2,4,6,8,12,14,16,18,10,10]
the return will be 1
. (**
is exponential operator)
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr)); // << array iteration * 2
let counts = uniqueVals.map(function (c, i, a) { // << outer iteration
return arr.filter(function (c2) { // << inner iteration **2
return c2 == c
}).length
});
if (arr.every(sameValues)) { // << array iteration
return 0;
} else {
var mode;
if ((counts.filter(function (x) { // << outer iteration
return x == (Math.max(...counts)) // << inner iteration **2 to get max
}).length) == 1) {
// Next line has nested iteration **2 to get max again
mode = uniqueVals[counts.indexOf(Math.max(...counts))];
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { // << array iteration
return a + c
})) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
Not only are the 3 instances of iteration at O(n2) unneeded, you use Array.filter
to count!! :( ... Use Array.reduce
to count so you don't need to allocate memory each time you add 1.
There are also repeated iterations that compute the same value. Math.max(...counts)
requires one full iteration of counts
each time it is called and you call it in the worst case 2n times.
Even when you add these optimisations (Array.reduce
rather than Array.filter
and calculate max once) and gain about 50% performance you are still at O(n2).
In a single pass O(n)
Using a counts = new Map();
and a single iteration you can compute the sum
to get the mean (after iteration), count each items frequency (via the map), and as you count the frequency you can keep a record of the top two max counts and the current max mode
.
After the iteration compute the mean
from the sum
divide the arr.length
, check if mode
is equal to mean
and if it is check that the top two frequencies are not equal to return 1 (or true).
An example of O(n) solution tuned for performance. (has optimisation that give an advantage if there are many sequences of the same value)
function isMeanMode(arr) {
const counts = new Map();
var i, prevMaxVal, item, maxA = -1, maxB = -1, index = 0, max = 1, sum = 0;
for (i = 0; i < arr.length; i ++) { // Only one iteration O(n)
const val = arr[i];
const item = counts.get(val);
sum += val;
if (item) {
item.count++;
while (val === arr[i + 1]) { // for quick count of sequences
i++;
item.count++;
sum += val;
}
if (max <= item.count) {
max = item.count;
if (prevMaxVal !== val) { // tracks the top two counts
maxB = maxA; // if maxA and maxB are the same
maxA = max; // after iteration then 2 or more mode vals
} else { maxA = max }
index = i;
prevMaxVal = val;
}
} else { counts.set(val, {count : 1}) }
}
if (counts.size === arr.length || maxB !== -1 && maxA === maxB) { return 0 }
return arr[index] === sum / arr.length ? 1 : 0;
}
The above solution has a performance increase from 2 orders of magnitude + faster for large random arrays, to 3-4 times faster for very small arrays.
thank you - very interesting information
– user8758206
Nov 25 at 11:11
add a comment |
up vote
1
down vote
Too complex
As the issue of complexity has not come up and as the functions complexity is O(n2) for a problem that can be solved in O(n) where n is length of arr
, I must point out some of the flaws.
The code comments show all the iterations for a worst case argument [2,4,6,8,12,14,16,18,10,10]
the return will be 1
. (**
is exponential operator)
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr)); // << array iteration * 2
let counts = uniqueVals.map(function (c, i, a) { // << outer iteration
return arr.filter(function (c2) { // << inner iteration **2
return c2 == c
}).length
});
if (arr.every(sameValues)) { // << array iteration
return 0;
} else {
var mode;
if ((counts.filter(function (x) { // << outer iteration
return x == (Math.max(...counts)) // << inner iteration **2 to get max
}).length) == 1) {
// Next line has nested iteration **2 to get max again
mode = uniqueVals[counts.indexOf(Math.max(...counts))];
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { // << array iteration
return a + c
})) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
Not only are the 3 instances of iteration at O(n2) unneeded, you use Array.filter
to count!! :( ... Use Array.reduce
to count so you don't need to allocate memory each time you add 1.
There are also repeated iterations that compute the same value. Math.max(...counts)
requires one full iteration of counts
each time it is called and you call it in the worst case 2n times.
Even when you add these optimisations (Array.reduce
rather than Array.filter
and calculate max once) and gain about 50% performance you are still at O(n2).
In a single pass O(n)
Using a counts = new Map();
and a single iteration you can compute the sum
to get the mean (after iteration), count each items frequency (via the map), and as you count the frequency you can keep a record of the top two max counts and the current max mode
.
After the iteration compute the mean
from the sum
divide the arr.length
, check if mode
is equal to mean
and if it is check that the top two frequencies are not equal to return 1 (or true).
An example of O(n) solution tuned for performance. (has optimisation that give an advantage if there are many sequences of the same value)
function isMeanMode(arr) {
const counts = new Map();
var i, prevMaxVal, item, maxA = -1, maxB = -1, index = 0, max = 1, sum = 0;
for (i = 0; i < arr.length; i ++) { // Only one iteration O(n)
const val = arr[i];
const item = counts.get(val);
sum += val;
if (item) {
item.count++;
while (val === arr[i + 1]) { // for quick count of sequences
i++;
item.count++;
sum += val;
}
if (max <= item.count) {
max = item.count;
if (prevMaxVal !== val) { // tracks the top two counts
maxB = maxA; // if maxA and maxB are the same
maxA = max; // after iteration then 2 or more mode vals
} else { maxA = max }
index = i;
prevMaxVal = val;
}
} else { counts.set(val, {count : 1}) }
}
if (counts.size === arr.length || maxB !== -1 && maxA === maxB) { return 0 }
return arr[index] === sum / arr.length ? 1 : 0;
}
The above solution has a performance increase from 2 orders of magnitude + faster for large random arrays, to 3-4 times faster for very small arrays.
thank you - very interesting information
– user8758206
Nov 25 at 11:11
add a comment |
up vote
1
down vote
up vote
1
down vote
Too complex
As the issue of complexity has not come up and as the functions complexity is O(n2) for a problem that can be solved in O(n) where n is length of arr
, I must point out some of the flaws.
The code comments show all the iterations for a worst case argument [2,4,6,8,12,14,16,18,10,10]
the return will be 1
. (**
is exponential operator)
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr)); // << array iteration * 2
let counts = uniqueVals.map(function (c, i, a) { // << outer iteration
return arr.filter(function (c2) { // << inner iteration **2
return c2 == c
}).length
});
if (arr.every(sameValues)) { // << array iteration
return 0;
} else {
var mode;
if ((counts.filter(function (x) { // << outer iteration
return x == (Math.max(...counts)) // << inner iteration **2 to get max
}).length) == 1) {
// Next line has nested iteration **2 to get max again
mode = uniqueVals[counts.indexOf(Math.max(...counts))];
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { // << array iteration
return a + c
})) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
Not only are the 3 instances of iteration at O(n2) unneeded, you use Array.filter
to count!! :( ... Use Array.reduce
to count so you don't need to allocate memory each time you add 1.
There are also repeated iterations that compute the same value. Math.max(...counts)
requires one full iteration of counts
each time it is called and you call it in the worst case 2n times.
Even when you add these optimisations (Array.reduce
rather than Array.filter
and calculate max once) and gain about 50% performance you are still at O(n2).
In a single pass O(n)
Using a counts = new Map();
and a single iteration you can compute the sum
to get the mean (after iteration), count each items frequency (via the map), and as you count the frequency you can keep a record of the top two max counts and the current max mode
.
After the iteration compute the mean
from the sum
divide the arr.length
, check if mode
is equal to mean
and if it is check that the top two frequencies are not equal to return 1 (or true).
An example of O(n) solution tuned for performance. (has optimisation that give an advantage if there are many sequences of the same value)
function isMeanMode(arr) {
const counts = new Map();
var i, prevMaxVal, item, maxA = -1, maxB = -1, index = 0, max = 1, sum = 0;
for (i = 0; i < arr.length; i ++) { // Only one iteration O(n)
const val = arr[i];
const item = counts.get(val);
sum += val;
if (item) {
item.count++;
while (val === arr[i + 1]) { // for quick count of sequences
i++;
item.count++;
sum += val;
}
if (max <= item.count) {
max = item.count;
if (prevMaxVal !== val) { // tracks the top two counts
maxB = maxA; // if maxA and maxB are the same
maxA = max; // after iteration then 2 or more mode vals
} else { maxA = max }
index = i;
prevMaxVal = val;
}
} else { counts.set(val, {count : 1}) }
}
if (counts.size === arr.length || maxB !== -1 && maxA === maxB) { return 0 }
return arr[index] === sum / arr.length ? 1 : 0;
}
The above solution has a performance increase from 2 orders of magnitude + faster for large random arrays, to 3-4 times faster for very small arrays.
Too complex
As the issue of complexity has not come up and as the functions complexity is O(n2) for a problem that can be solved in O(n) where n is length of arr
, I must point out some of the flaws.
The code comments show all the iterations for a worst case argument [2,4,6,8,12,14,16,18,10,10]
the return will be 1
. (**
is exponential operator)
function meanMode(arr) {
let uniqueVals = Array.from(new Set(arr)); // << array iteration * 2
let counts = uniqueVals.map(function (c, i, a) { // << outer iteration
return arr.filter(function (c2) { // << inner iteration **2
return c2 == c
}).length
});
if (arr.every(sameValues)) { // << array iteration
return 0;
} else {
var mode;
if ((counts.filter(function (x) { // << outer iteration
return x == (Math.max(...counts)) // << inner iteration **2 to get max
}).length) == 1) {
// Next line has nested iteration **2 to get max again
mode = uniqueVals[counts.indexOf(Math.max(...counts))];
} else {
return 0;
}
const mean = (arr.reduce(function (a, c) { // << array iteration
return a + c
})) / (arr.length);
if (mode == mean) {
return 1;
} else {
return 0;
}
}
function sameValues(c, i, a) {
return c == a[0];
}
}
Not only are the 3 instances of iteration at O(n2) unneeded, you use Array.filter
to count!! :( ... Use Array.reduce
to count so you don't need to allocate memory each time you add 1.
There are also repeated iterations that compute the same value. Math.max(...counts)
requires one full iteration of counts
each time it is called and you call it in the worst case 2n times.
Even when you add these optimisations (Array.reduce
rather than Array.filter
and calculate max once) and gain about 50% performance you are still at O(n2).
In a single pass O(n)
Using a counts = new Map();
and a single iteration you can compute the sum
to get the mean (after iteration), count each items frequency (via the map), and as you count the frequency you can keep a record of the top two max counts and the current max mode
.
After the iteration compute the mean
from the sum
divide the arr.length
, check if mode
is equal to mean
and if it is check that the top two frequencies are not equal to return 1 (or true).
An example of O(n) solution tuned for performance. (has optimisation that give an advantage if there are many sequences of the same value)
function isMeanMode(arr) {
const counts = new Map();
var i, prevMaxVal, item, maxA = -1, maxB = -1, index = 0, max = 1, sum = 0;
for (i = 0; i < arr.length; i ++) { // Only one iteration O(n)
const val = arr[i];
const item = counts.get(val);
sum += val;
if (item) {
item.count++;
while (val === arr[i + 1]) { // for quick count of sequences
i++;
item.count++;
sum += val;
}
if (max <= item.count) {
max = item.count;
if (prevMaxVal !== val) { // tracks the top two counts
maxB = maxA; // if maxA and maxB are the same
maxA = max; // after iteration then 2 or more mode vals
} else { maxA = max }
index = i;
prevMaxVal = val;
}
} else { counts.set(val, {count : 1}) }
}
if (counts.size === arr.length || maxB !== -1 && maxA === maxB) { return 0 }
return arr[index] === sum / arr.length ? 1 : 0;
}
The above solution has a performance increase from 2 orders of magnitude + faster for large random arrays, to 3-4 times faster for very small arrays.
answered Nov 24 at 16:20
Blindman67
6,6341521
6,6341521
thank you - very interesting information
– user8758206
Nov 25 at 11:11
add a comment |
thank you - very interesting information
– user8758206
Nov 25 at 11:11
thank you - very interesting information
– user8758206
Nov 25 at 11:11
thank you - very interesting information
– user8758206
Nov 25 at 11:11
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208244%2fsimple-function-returning-1-if-the-mean-mode-or-0-if-not%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown