Related
I'm looking for an elegant way of determining which element has the highest occurrence (mode) in a JavaScript array.
For example, in
['pear', 'apple', 'orange', 'apple']
the 'apple' element is the most frequent one.
This is just the mode. Here's a quick, non-optimized solution. It should be O(n).
function mode(array)
{
if(array.length == 0)
return null;
var modeMap = {};
var maxEl = array[0], maxCount = 1;
for(var i = 0; i < array.length; i++)
{
var el = array[i];
if(modeMap[el] == null)
modeMap[el] = 1;
else
modeMap[el]++;
if(modeMap[el] > maxCount)
{
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
There have been some developments in javascript since 2009 - I thought I'd add another option. I'm less concerned with efficiency until it's actually a problem so my definition of "elegant" code (as stipulated by the OP) favours readability - which is of course subjective...
function mode(arr){
return arr.sort((a,b) =>
arr.filter(v => v===a).length
- arr.filter(v => v===b).length
).pop();
}
mode(['pear', 'apple', 'orange', 'apple']); // apple
In this particular example, should two or more elements of the set have equal occurrences then the one that appears latest in the array will be returned. It's also worth pointing out that it will modify your original array - which can be prevented if you wish with an Array.slice call beforehand.
Edit: updated the example with some ES6 fat arrows because 2015 happened and I think they look pretty... If you are concerned with backwards compatibility you can find this in the revision history.
As per George Jempty's request to have the algorithm account for ties, I propose a modified version of Matthew Flaschen's algorithm.
function modeString(array) {
if (array.length == 0) return null;
var modeMap = {},
maxEl = array[0],
maxCount = 1;
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
maxEl += "&" + el;
maxCount = modeMap[el];
}
}
return maxEl;
}
This will now return a string with the mode element(s) delimited by a & symbol. When the result is received it can be split on that & element and you have your mode(s).
Another option would be to return an array of mode element(s) like so:
function modeArray(array) {
if (array.length == 0) return null;
var modeMap = {},
maxCount = 1,
modes = [];
for (var i = 0; i < array.length; i++) {
var el = array[i];
if (modeMap[el] == null) modeMap[el] = 1;
else modeMap[el]++;
if (modeMap[el] > maxCount) {
modes = [el];
maxCount = modeMap[el];
} else if (modeMap[el] == maxCount) {
modes.push(el);
maxCount = modeMap[el];
}
}
return modes;
}
In the above example you would then be able to handle the result of the function as an array of modes.
Based on Emissary's ES6+ answer, you could use Array.prototype.reduce to do your comparison (as opposed to sorting, popping and potentially mutating your array), which I think looks quite slick.
const mode = (myArray) =>
myArray.reduce(
(a,b,i,arr)=>
(arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b),
null)
I'm defaulting to null, which won't always give you a truthful response if null is a possible option you're filtering for, maybe that could be an optional second argument
The downside, as with various other solutions, is that it doesn't handle 'draw states', but this could still be achieved with a slightly more involved reduce function.
a=['pear', 'apple', 'orange', 'apple'];
b={};
max='', maxi=0;
for(let k of a) {
if(b[k]) b[k]++; else b[k]=1;
if(maxi < b[k]) { max=k; maxi=b[k] }
}
As I'm using this function as a quiz for the interviewers, I post my solution:
const highest = arr => (arr || []).reduce( ( acc, el ) => {
acc.k[el] = acc.k[el] ? acc.k[el] + 1 : 1
acc.max = acc.max ? acc.max < acc.k[el] ? el : acc.max : el
return acc
}, { k:{} }).max
const test = [0,1,2,3,4,2,3,1,0,3,2,2,2,3,3,2]
console.log(highest(test))
Trying out a declarative approach here. This solution builds an object to tally up the occurrences of each word. Then filters the object down to an array by comparing the total occurrences of each word to the highest value found in the object.
const arr = ['hello', 'world', 'hello', 'again'];
const tally = (acc, x) => {
if (! acc[x]) {
acc[x] = 1;
return acc;
}
acc[x] += 1;
return acc;
};
const totals = arr.reduce(tally, {});
const keys = Object.keys(totals);
const values = keys.map(x => totals[x]);
const results = keys.filter(x => totals[x] === Math.max(...values));
This solution has O(n) complexity:
function findhighestOccurenceAndNum(a) {
let obj = {};
let maxNum, maxVal;
for (let v of a) {
obj[v] = ++obj[v] || 1;
if (maxVal === undefined || obj[v] > maxVal) {
maxNum = v;
maxVal = obj[v];
}
}
console.log(maxNum + ' has max value = ' + maxVal);
}
findhighestOccurenceAndNum(['pear', 'apple', 'orange', 'apple']);
For the sake of really easy to read, maintainable code I share this:
function getMaxOcurrences(arr = []) {
let item = arr[0];
let ocurrencesMap = {};
for (let i in arr) {
const current = arr[i];
if (ocurrencesMap[current]) ocurrencesMap[current]++;
else ocurrencesMap[current] = 1;
if (ocurrencesMap[item] < ocurrencesMap[current]) item = current;
}
return {
item: item,
ocurrences: ocurrencesMap[item]
};
}
Hope it helps someone ;)!
Here’s the modern version using built-in maps (so it works on more than things that can be converted to unique strings):
'use strict';
const histogram = iterable => {
const result = new Map();
for (const x of iterable) {
result.set(x, (result.get(x) || 0) + 1);
}
return result;
};
const mostCommon = iterable => {
let maxCount = 0;
let maxKey;
for (const [key, count] of histogram(iterable)) {
if (count > maxCount) {
maxCount = count;
maxKey = key;
}
}
return maxKey;
};
console.log(mostCommon(['pear', 'apple', 'orange', 'apple']));
Time for another solution:
function getMaxOccurrence(arr) {
var o = {}, maxCount = 0, maxValue, m;
for (var i=0, iLen=arr.length; i<iLen; i++) {
m = arr[i];
if (!o.hasOwnProperty(m)) {
o[m] = 0;
}
++o[m];
if (o[m] > maxCount) {
maxCount = o[m];
maxValue = m;
}
}
return maxValue;
}
If brevity matters (it doesn't), then:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
return mV;
}
If non–existent members are to be avoided (e.g. sparse array), an additional hasOwnProperty test is required:
function getMaxOccurrence(a) {
var o = {}, mC = 0, mV, m;
for (var i=0, iL=a.length; i<iL; i++) {
if (a.hasOwnProperty(i)) {
m = a[i];
o.hasOwnProperty(m)? ++o[m] : o[m] = 1;
if (o[m] > mC) mC = o[m], mV = m;
}
}
return mV;
}
getMaxOccurrence([,,,,,1,1]); // 1
Other answers here will return undefined.
Here is another ES6 way of doing it with O(n) complexity
const result = Object.entries(
['pear', 'apple', 'orange', 'apple'].reduce((previous, current) => {
if (previous[current] === undefined) previous[current] = 1;
else previous[current]++;
return previous;
}, {})).reduce((previous, current) => (current[1] >= previous[1] ? current : previous))[0];
console.log("Max value : " + result);
function mode(arr){
return arr.reduce(function(counts,key){
var curCount = (counts[key+''] || 0) + 1;
counts[key+''] = curCount;
if (curCount > counts.max) { counts.max = curCount; counts.mode = key; }
return counts;
}, {max:0, mode: null}).mode
}
Another JS solution from: https://www.w3resource.com/javascript-exercises/javascript-array-exercise-8.php
Can try this too:
let arr =['pear', 'apple', 'orange', 'apple'];
function findMostFrequent(arr) {
let mf = 1;
let m = 0;
let item;
for (let i = 0; i < arr.length; i++) {
for (let j = i; j < arr.length; j++) {
if (arr[i] == arr[j]) {
m++;
if (m > mf) {
mf = m;
item = arr[i];
}
}
}
m = 0;
}
return item;
}
findMostFrequent(arr); // apple
This solution can return multiple elements of an array in case of a tie. For example, an array
arr = [ 3, 4, 3, 6, 4, ];
has two mode values: 3 and 6.
Here is the solution.
function find_mode(arr) {
var max = 0;
var maxarr = [];
var counter = [];
var maxarr = [];
arr.forEach(function(){
counter.push(0);
});
for(var i = 0;i<arr.length;i++){
for(var j=0;j<arr.length;j++){
if(arr[i]==arr[j])counter[i]++;
}
}
max=this.arrayMax(counter);
for(var i = 0;i<arr.length;i++){
if(counter[i]==max)maxarr.push(arr[i]);
}
var unique = maxarr.filter( this.onlyUnique );
return unique;
};
function arrayMax(arr) {
var len = arr.length, max = -Infinity;
while (len--) {
if (arr[len] > max) {
max = arr[len];
}
}
return max;
};
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
const frequence = (array) =>
array.reduce(
(acc, item) =>
array.filter((v) => v === acc).length >=
array.filter((v) => v === item).length
? acc
: item,
null
);
frequence([1, 1, 2])
var array = [1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17],
c = {}, // counters
s = []; // sortable array
for (var i=0; i<array.length; i++) {
c[array[i]] = c[array[i]] || 0; // initialize
c[array[i]]++;
} // count occurrences
for (var key in c) {
s.push([key, c[key]])
} // build sortable array from counters
s.sort(function(a, b) {return b[1]-a[1];});
var firstMode = s[0][0];
console.log(firstMode);
Here is my solution to this problem but with numbers and using the new 'Set' feature. Its not very performant but i definitely had a lot of fun writing this and it does support multiple maximum values.
const mode = (arr) => [...new Set(arr)]
.map((value) => [value, arr.filter((v) => v === value).length])
.sort((a,b) => a[1]-b[1])
.reverse()
.filter((value, i, a) => a.indexOf(value) === i)
.filter((v, i, a) => v[1] === a[0][1])
.map((v) => v[0])
mode([1,2,3,3]) // [3]
mode([1,1,1,1,2,2,2,2,3,3,3]) // [1,2]
By the way do not use this for production this is just an illustration of how you can solve it with ES6 and Array functions only.
const mode = (str) => {
return str
.split(' ')
.reduce((data, key) => {
let counter = data.map[key] + 1 || 1
data.map[key] = counter
if (counter > data.counter) {
data.counter = counter
data.mode = key
}
return data
}, {
counter: 0,
mode: null,
map: {}
})
.mode
}
console.log(mode('the t-rex is the greatest of them all'))
Here is my solution :-
function frequent(number){
var count = 0;
var sortedNumber = number.sort();
var start = number[0], item;
for(var i = 0 ; i < sortedNumber.length; i++){
if(start === sortedNumber[i] || sortedNumber[i] === sortedNumber[i+1]){
item = sortedNumber[i]
}
}
return item
}
console.log( frequent(['pear', 'apple', 'orange', 'apple']))
Try it too, this does not take in account browser version.
function mode(arr){
var a = [],b = 0,occurrence;
for(var i = 0; i < arr.length;i++){
if(a[arr[i]] != undefined){
a[arr[i]]++;
}else{
a[arr[i]] = 1;
}
}
for(var key in a){
if(a[key] > b){
b = a[key];
occurrence = key;
}
}
return occurrence;
}
alert(mode(['segunda','terça','terca','segunda','terça','segunda']));
Please note that this function returns latest occurence in the array
when 2 or more entries appear same number of times!
With ES6, you can chain the method like this:
function findMostFrequent(arr) {
return arr
.reduce((acc, cur, ind, arr) => {
if (arr.indexOf(cur) === ind) {
return [...acc, [cur, 1]];
} else {
acc[acc.indexOf(acc.find(e => e[0] === cur))] = [
cur,
acc[acc.indexOf(acc.find(e => e[0] === cur))][1] + 1
];
return acc;
}
}, [])
.sort((a, b) => b[1] - a[1])
.filter((cur, ind, arr) => cur[1] === arr[0][1])
.map(cur => cur[0]);
}
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple']));
console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple', 'pear']));
If two elements have the same occurrence, it will return both of them. And it works with any type of element.
// O(n)
var arr = [1, 2, 3, 2, 3, 3, 5, 6];
var duplicates = {};
max = '';
maxi = 0;
arr.forEach((el) => {
duplicates[el] = duplicates[el] + 1 || 1;
if (maxi < duplicates[el]) {
max = el;
maxi = duplicates[el];
}
});
console.log(max);
I came up with a shorter solution, but it's using lodash. Works with any data, not just strings. For objects can be used:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el.someUniqueProp)), arr => arr.length)[0];
This is for strings:
const mostFrequent = _.maxBy(Object.values(_.groupBy(inputArr, el => el)), arr => arr.length)[0];
Just grouping data under a certain criteria, then finding the largest group.
Here is my way to do it so just using .filter.
var arr = ['pear', 'apple', 'orange', 'apple'];
function dup(arrr) {
let max = { item: 0, count: 0 };
for (let i = 0; i < arrr.length; i++) {
let arrOccurences = arrr.filter(item => { return item === arrr[i] }).length;
if (arrOccurences > max.count) {
max = { item: arrr[i], count: arrr.filter(item => { return item === arrr[i] }).length };
}
}
return max.item;
}
console.log(dup(arr));
Easy solution !
function mostFrequentElement(arr) {
let res = [];
for (let x of arr) {
let count = 0;
for (let i of arr) {
if (i == x) {
count++;
}
}
res.push(count);
}
return arr[res.indexOf(Math.max(...res))];
}
array = [13 , 2 , 1 , 2 , 10 , 2 , 1 , 1 , 2 , 2];
let frequentElement = mostFrequentElement(array);
console.log(`The frequent element in ${array} is ${frequentElement}`);
Loop on all element and collect the Count of each element in the array that is the idea of the solution
Here is my solution :-
const arr = [
2, 1, 10, 7, 10, 3, 10, 8, 7, 3, 10, 5, 4, 6, 7, 9, 2, 2, 2, 6, 3, 7, 6, 9, 8,
9, 10, 8, 8, 8, 4, 1, 9, 3, 4, 5, 8, 1, 9, 3, 2, 8, 1, 9, 6, 3, 9, 2, 3, 5, 3,
2, 7, 2, 5, 4, 5, 5, 8, 4, 6, 3, 9, 2, 3, 3, 10, 3, 3, 1, 4, 5, 4, 1, 5, 9, 6,
2, 3, 10, 9, 4, 3, 4, 5, 7, 2, 7, 2, 9, 8, 1, 8, 3, 3, 3, 3, 1, 1, 3,
];
function max(arr) {
let newObj = {};
arr.forEach((d, i) => {
if (newObj[d] != undefined) {
++newObj[d];
} else {
newObj[d] = 0;
}
});
let nwres = {};
for (let maxItem in newObj) {
if (newObj[maxItem] == Math.max(...Object.values(newObj))) {
nwres[maxItem] = newObj[maxItem];
}
}
return nwres;
}
console.log(max(arr));
I guess you have two approaches. Both of which have advantages.
Sort then Count or Loop through and use a hash table to do the counting for you.
The hashtable is nice because once you are done processing you also have all the distinct elements. If you had millions of items though, the hash table could end up using a lot of memory if the duplication rate is low. The sort, then count approach would have a much more controllable memory footprint.
var mode = 0;
var c = 0;
var num = new Array();
var value = 0;
var greatest = 0;
var ct = 0;
Note: ct is the length of the array.
function getMode()
{
for (var i = 0; i < ct; i++)
{
value = num[i];
if (i != ct)
{
while (value == num[i + 1])
{
c = c + 1;
i = i + 1;
}
}
if (c > greatest)
{
greatest = c;
mode = value;
}
c = 0;
}
}
You can try this:
// using splice()
// get the element with the highest occurence in an array
function mc(a) {
var us = [], l;
// find all the unique elements in the array
a.forEach(function (v) {
if (us.indexOf(v) === -1) {
us.push(v);
}
});
l = us.length;
while (true) {
for (var i = 0; i < l; i ++) {
if (a.indexOf(us[i]) === -1) {
continue;
} else if (a.indexOf(us[i]) != -1 && a.length > 1) {
// just delete it once at a time
a.splice(a.indexOf(us[i]), 1);
} else {
// default to last one
return a[0];
}
}
}
}
// using string.match method
function su(a) {
var s = a.join(),
uelms = [],
r = {},
l,
i,
m;
a.forEach(function (v) {
if (uelms.indexOf(v) === -1) {
uelms.push(v);
}
});
l = uelms.length;
// use match to calculate occurance times
for (i = 0; i < l; i ++) {
r[uelms[i]] = s.match(new RegExp(uelms[i], 'g')).length;
}
m = uelms[0];
for (var p in r) {
if (r[p] > r[m]) {
m = p;
} else {
continue;
}
}
return m;
}
I have a function that mimics the array_count_values function from php in javascript but it's not very fast. I'm wondering if there's a way to fix it?
function array_count_values(arr) {
let a = [], prev;
arr.sort();
for ( let i = 0; i < arr.length; i++ ) {
if ( arr[i] !== prev ) {
a.push(1)
} else {
a[a.length-1]++;
}
prev = arr[i];
}
return a;
}
It just returns a simple array of numbers with the counts so like 2,1,2,1,1. The input in this case would be numeric arrays 5-7 elements long, so for example array_count_values([6,4,10,6,6])
You can use reduce to loop thru the array and count each entry.
function array_count_values(arr) {
return arr.reduce((c, v) => {
c[v] = c[v] || 0;
c[v]++;
return c;
}, {})
}
var result = array_count_values([6, 4, 10, 6, 6]);
console.log(result);
You could take an object for counting and omit sorting. This approach uses a single loop.
function array_count_values(array) {
var count = {},
i;
for (i = 0; i < array.length; i++) {
if (array[i] in count) {
count[array[i]]++;
} else {
count[array[i]] = 1;
}
}
return Object.values(count).sort((a, b) => b - a);
}
console.log(array_count_values([6, 4, 10, 6, 6]));
This is actually a straight-forward algorithm. I've been brushing up on them lately:
var array_count_values = function(array) {
let dict = {};
for (var i = 0; i < array.length; i++ ) {
var num = array[i];
(dict[num]) ? dict[num]++ : dict[num] = 1;
}
return dict;
}
console.log(array_count_values([6, 4, 10, 6, 6]));
Time and space complexity is both O(n).
I think the addition of a sort here is overkill, and probably the slowest part of this.
I think this will be the fastest/simplest way you can do this.
function array_count_values(arr) {
let outputCounts = {};
for ( let i = 0; i < arr.length; i++ ) {
if (outputCounts[arr[i]] != undefined){
outputCounts[arr[i]] += 1;
} else {
outputCounts[arr[i]] = 1;
}
}
return outputCounts;
}
The caveat here is that you're going to get an object back instead of an array as in your example.
const arr = [1, 2, 2, 3];
function array_count_values (arr) {
const frequencies = arr.reduce((f, v) => {
const freq = f.get(v) || 0;
f.set(v, freq + 1);
return f;
}, new Map());
return arr.map(v => frequencies.get(v));
}
console.log(array_count_values(arr));
Looking at how array_count_values works in php. This might be what you are looking for
function array_count_values(arr) {
return arr.reduce((acc, val) => {
if (!acc[val]) {
acc[val] = 0
}
acc[val] += 1
return acc
}, {})
}
To return an array as required in the question
function array_count_values(arr) {
return Object.values(arr.reduce((acc, val) => {
if (!acc[val]) {
acc[val] = 0
}
acc[val] += 1
return acc
}, {}))
}
my question is actually similar to: Extracting the most duplicate value from an array in JavaScript (with jQuery)
I Found this but it always return one value only which is 200.
var arr = [100,100,200,200,200,300,300,300,400,400,400];
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];
}
}
console.log(res + " occurs " + counts[res] + " times");
pls help me to return values not just one...
The result is should like this:
200,300,400
.
pls help thank you!
You have to iterate your counts to find the max occurred result.
var arr = [100,100,200,200,200,300,300,300,400,400,400];
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];
}
}
var results = [];
for (var k in counts){
if (counts[k] == max){
//console.log(k + " occurs " + counts[k] + " times");
results.push(k);
}
}
console.log(results);
Create a Object iterating the arry containing the indexes of most repeated values, like below
var arr = [100,100,200,200,200,300,300,300,400,400,400];
valObj = {}, max_length = 0, rep_arr = [];
arr.forEach(function(el,i){
if(valObj.hasOwnProperty(el)){
valObj[el] += 1;
max_length = (valObj[el] > max_length) ? valObj[el] : max_length
}
else{
valObj[el] = 1;
}
});
Object.keys(valObj).forEach(function(val){
(valObj[val] >= max_length) && (rep_arr.push(val))
});
console.log(rep_arr);
After the object is created with key as array value and value as array indexes of that value, you can play/parse that. Hope this helps.
Iterating an array using for..in is not a good idea. Check this link for more information.
Hopefully below snippet will be useful
var arr = [100, 100, 200, 200, 200, 300, 300, 300, 400, 400, 400];
//Use a reduce fuction to create an object where 100,200,300
// will be keys and its value will the number of times it has
//repeated
var m = arr.reduce(function(i, v) {
if (i[v] === undefined) {
i[v] = 1
} else {
i[v] = i[v] + 1;
}
return i;
}, {});
// Now get the maximum value from that object,
//getMaxRepeated will be 3 in this case
var getMaxRepeated = Math.max(...Object.values(m));
//An array to hold elements which are repeated 'getMaxRepeated' times
var duplicateItems = [];
// now iterate that object and push the keys which are repeated
//getMaxRepeated times
for (var keys in m) {
if (m[keys] === getMaxRepeated) {
duplicateItems.push(keys)
}
}
console.log(duplicateItems)
The following would do the trick assuming that all items in arr are numbers:
//added some numbers assuming numbers are not sorted
var arr = [300,400,200,100,100,200,200,200,300,300,300,400,400,400];
var obj = arr.reduce(//reduce arr to object of: {"100":2,"200":4,"300":4,"400":4}
(o,key)=>{//key is 100,200, ... o is {"100":numberOfOccurrences,"200":numberOf...}
o[key] = (o[key])?o[key]+1:1;
return o;
},
{}
);
// obj is now: {"100":2,"200":4,"300":4,"400":4}
//create an array of [{key:100,occurs:2},{key:200,occurs:4}...
var sorted = Object.keys(obj).map(
key=>({key:parseInt(key),occurs:obj[key]})
)//sort the [{key:100,occurs:2},... by highest occurrences then lowest key
.sort(
(a,b)=>
(b.occurs-a.occurs===0)
? a.key - b.key
: b.occurs - a.occurs
);
console.log(
sorted.filter(//only the highest occurrences
item=>item.occurs===sorted[0].occurs
).map(//only the number; not the occurrences
item=>item.key
)
);
Try as following ==>
function getDuplicate( arr ){
let obj = {}, dup = [];
for(let i = 0, l = arr.length; i < l; i++){
let val = arr[i];
if( obj[val] /**[hasOwnProperty]*/ ) {
/**[is exists]*/
if(dup.find(a => a == val) ) continue;
/**[put Unique One]*/
dup.push(val);
continue;
};
/**[hold for further use]*/
obj[val] = true;
}
return dup;
};
Use ==>
getDuplicate([100,100,200,200,200,300,300,300,400,400,400]);
Try the following:
var candles = [100,100,200,200,200,300,300,300,400,400,400];
let tempArray = {}
for (let index = 0; index <= (candles.length - 1); index++) {
let valueToCompare = candles[index];
if (tempArray[valueToCompare]) {
tempArray[valueToCompare] = tempArray[valueToCompare] + 1;
} else {
tempArray[valueToCompare] = 1;
}
}
let highestValue;
Object.values(tempArray).forEach(item => {
if (highestValue === undefined) highestValue = item;
if (highestValue < item) highestValue = item;
});
console.log(highestValue);
I have a constant array like this:
const pie_values = [20,10,5,5,10];
The challenge is to transform the above array based on integer input.
(5) => [5, 15, 10, 5, 5, 10]
(21) => [20, 1, 9, 5, 5, 10]
(31) => [20, 10, 1, 4, 5, 10]
An input of 5 takes 5 from the first index of pie_values. But it still leaves 15.
An input of 21 can take 20 from index 0 and 1 from index 2, leaving 9
I think you can see how this is going. So (0) and (50) will return the original pie_values.
Now the challenge is to create a function that does this in a few lines of code, which is build on loops rather than on 5 if statements. In case pie_values is extended upon, the function should still work.
I have an approach working with if statements, however the latter is undoable. What would be a good approach to these kind of problems?
First I defined a helper function:
//Returns summation of pie value
// totalPieValues(1) = 20
// totalPieValues(2) = 30
// totalPieValues(3) = 35
// totalPieValues(4) = 40
// totalPieValues(5) = 50
function totalPieValues(max) {
let result = 0;
for (let i = 0; i < max; i++) {
result += PIE_VALUES[i];
}
return result;
}
Then I worked on a function getPieArray which utilizes the helper function. This is where I am stuck
function getPieArray(wp) {
for (let i = 0; i < PIE_VALUES.length; i++) {
if (wp == 0 || wp == totalPieValues(i)) {
return PIE_VALUES;
}
}
let result = [];
for (let i = 1; i <= PIE_VALUES.length; i++) {
if (wp > totalPieValues(PIE_VALUES.length - i)) {
result.push(PIE_VALUES[i]);
} else if () {
result.push(wp - totalPieValues(3));
} else {
result.push(PIE_VALUES[i] - (value - totalPieValues(3)));
}
}
return result;
}
The code that I have written and works is here:
//Returns array of exact values needed to show in pie chart
export function getPieValues(wp) {
//1 => [1, 19, 10, 5, 5, 10]
//24 => [20, 4, 1, 5, 5, 10]
//31 => [20, 10, 1, 5, 5, 5, 10]
let result;
if (wp == 0) {
result = PIE_VALUES;
} else if (wp < totalPieValues(1)) {
result = [wp - totalPieValues(0), PIE_VALUES[0] - wp, PIE_VALUES[1], PIE_VALUES[2], PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(1)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(2)) {
result = [PIE_VALUES[0], wp - totalPieValues(1), PIE_VALUES[1] - (wp - PIE_VALUES[0]), PIE_VALUES[2], PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(2)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(3)) {
result = [PIE_VALUES[0], PIE_VALUES[1], wp - totalPieValues(2), PIE_VALUES[2] - (wp - totalPieValues(2)), PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(3)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(4)) {
result = [PIE_VALUES[0], PIE_VALUES[1], PIE_VALUES[2], wp - totalPieValues(3), PIE_VALUES[3] - (wp - totalPieValues(3)), PIE_VALUES[4]];
} else if (wp == totalPieValues(4)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(5)) {
result = [PIE_VALUES[0], PIE_VALUES[1], PIE_VALUES[2], PIE_VALUES[3], wp - totalPieValues(4), PIE_VALUES[4] - (wp - totalPieValues(4))];
} else if (wp == totalPieValues(5)) {
result = PIE_VALUES;
}
return result;
}
This is super overkill
You can just iterate through the array and "eat" the index value and continue
function pieArray(inputArray, value){
let copyOfValue = value;
return inputArray.reduce((sum, input, index) => { // <-- index here
copyOfValue -= input;
if(copyOfValue > 0){
sum.push(input);
}else{
sum.push(input+copyOfValue);
sum.push(Math.abs(copyOfValue));
copyOfValue = Number.MAX_VALUE; //Hacky solution, just change value to max value
}
return sum;
}, []);
}
Tests
pieArray([20,10,5,5,10], 5) => [5, 15, 10, 5, 5, 10]
pieArray([20,10,5,5,10], 21) => [20, 1, 9, 5, 5, 10]
pieArray([20,10,5,5,10], 31) => [20, 10, 1, 4, 5, 10]
This is my approach. We iterate over our array, keeping track of our current value - and substracting from it as we push out each element to the output array.
There is 3 cases:
either our current count is >= input, so we just push and move on,
current count is 0, so we just push everything left
current count is < input, but more than 0 - in this case we split.
Here is the code:
function transform(input, array) {
const total = array.reduce((previous, current) => previous + current);
// It wasn't specified what should happen when the input > total, so we will just throw an error.
if (input > total) {
throw new Error('Input cannot be bigger than the sum of elements in the array.');
}
let current = input;
let result = [];
for (let i = 0; i < array.length; i++) {
if (current >= array[i]) {
result.push(array[i]);
current -= array[i];
} else if (current === 0) {
result.push(array[i]);
} else {
result.push(current, array[i] - current);
current = 0;
}
}
return result;
}
Some of these answers are a bit overcomplicated. If you use a recursive function, you can do this in just two lines of code.
const pie_values = [20,10,5,5,10];
// The challenge is to transform the above array based on integer input.
// (5) => [5, 15, 10, 5, 5, 10]
// (21) => [20, 1, 9, 5, 5, 10]
// (31) => [20, 10, 1, 4, 5, 10]
function reshape(num, vals) {
if (num < vals[0]) return [num, vals[0] - num, ...vals.slice(1)];
return [vals[0], ...reshape(num - vals[0], vals.slice(1))];
}
console.log(reshape(5, pie_values))
console.log(reshape(21, pie_values))
console.log(reshape(31, pie_values))
The key is realizing that if the amount you need to take is less than the next value, then you can take it from that next value and the remainder of the array will stay the same.
But if you need to take more than what's available, take as much as you can get from the first value, and then take that much less from the remainder of the array.
EDIT: Note that if the number you give is larger than the sum of all the pie values, this will recurse infinitely (leading to a stack overflow). To be totally safe, you should ensure that the value is less than the total sum before calling reshape.
You want something like that ? (Works with the examples you've given)
function getPieValues(integer_input) {
"use strict";
let final_arr = pie_values,
array_sum = pie_values.reduce((pv, cv) => pv + cv , 0);
if(integer_input !== 0 && integer_input !== array_sum) { // For the cases 50 and 0, the array won't be modified
for(let i = 0; i < pie_values.length; i++) {
if(pie_values[i] < integer_input) { // if the prompted number is bigger than the current value, we keep up
integer_input -= pie_values[i];
} else { // When it becomes smaller, we add the remainder at the front of the current value, then we modify the next value, and finally we break it so that it doesn't happen next
final_arr.splice(i, 0, integer_input);
final_arr[i+1] -= integer_input;
break;
}
}
}
return final_arr;
}
Edit : Made it a function, and made it work with 0 and 50 (sorry, first post ;-) )
This one's pretty simple and efficient. It doesn't iterate the whole array, only up to the point it needs to.
const pie_values = [20,10,5,5,10];
function pied(n) {
var i = 0;
var total = pie_values[0];
while (total < n && i < pie_values.length) {
i++;
total += pie_values[i];
}
if (i < pie_values.length) {
var diff = total - n;
if (diff > 0 && n > 0) {
return [].concat(
pie_values.slice(0, i), // the part of the array up to i
pie_values[i] - diff, // the amount we used of the last element we needed
diff, // the amount left over
pie_values.slice(i + 1) // the rest of the array after i
);
} else {
// just return a copy of the original array
return pie_values.slice();
}
} else {
// n was greater than the total of all elements of the array
return "went over";
}
}
console.log(pied(5));
Using vanilla Javascript for-loop
Look at this code snippet
const pie_values = [20, 10, 5, 5, 10];
var fn = (input) => {
let array = [];
for (var i = 0; i < pie_values.length; i++) {
var n = pie_values[i];
let calc = n - input;
if (calc > 0) {
array.push(n - calc); // Push how many used, i.e n = 20, input = 10.
array.push(calc); // Push the remaining after subtraction.
array = array.concat(pie_values.slice(i + 1)); // Add the remaining values from 'pie_values'
return array;
} else {
array.push(n); // Push all this number because was insufficient, i.e n = 20, input = 30
input = Math.abs(calc); // Remaining for the next iteration.
}
}
return array;
};
console.log(fn(5));
console.log(fn(21));
console.log(fn(31));
console.log(fn(0));
console.log(fn(50));
const pie_values = [20,10,5,5,10];
function rebaseArr(input){
var retArr = [];
var total = 0;
let isDone = false;
for(var i in pie_values){
let currentVal = pie_values[i];
total += currentVal;
if(total > input && !isDone){
let rem = total - input;
let rem1 = currentVal - rem;
rem1 !== 0 ? retArr.push(rem1) : 0;
retArr.push(rem);
isDone = true;
} else {
retArr.push(currentVal);
}
}
return retArr;
}
console.log(rebaseArr(31));
console.log(rebaseArr(1));
console.log(rebaseArr(10));
Can you please try with above code.
Hope it helps :)
I wouldn't normally advise recursion in JS however just for fun you may implement an Haskellesque pattern matching by using spread and rest operators through destructuring and may come up with something like below;
It wasn't clear to me what to do when the difference is zero so being a remarkably lazy person i choose to do nothing. (Last test won't return [20,10,5,0,5,10])
var extend = ([x,...xs],n) => n && x ? x > n ? [n, x-n, ...xs]
: [x, ...extend(xs, n-x)]
: [x,...xs],
pvs = [20,10,5,5,10];
console.log(extend(pvs,5));
console.log(extend(pvs,21));
console.log(extend(pvs,31));
console.log(extend(pvs,35));
.as-console-wrapper {
max-height : 100% !important
}
[WITHOUT USING MIN/MATH Function]
The question ask me to create the function including 2 parameters.first is an array and the second parameter is either the String “Minimum” or “Maximum” . It confusing me when i don't know how to input the parameter as the string in the function. So that i decide to create 2 similar function as extremeValue(vector, maximum) and extremeValue(vector, minimum) ( I still don't know whether i can do that or not ).
And this is my code for extremeValue(vector, maximum).
So the way i did is create the array which have the same value with vector[i] than i using if statement to see if it bigger than that vector or not. However, The code is doesn't work. :(
var vector = [3, 1, 1]
var maximum
//------------this function give us the vector with all same value---------------//
function set(value, len) {
var arr = [];
for (var i = 0; i < len; i++) {
arr.push(value);
}
return arr;
}
//---------------------------------------------------//
//---------------------------------------------------//
function extremeValue(vector, maximum) {
var answer = "";
for (var count = 1; count++; count < vector.length) {
if (set(vector[count], vector.length) > vector)
answer += vector[count] + "is maximum"
}
return answer
}
//---------------------------------------------------//
console.log(extremeValue(vector, maximum))
Without using Math or any other javascript function, you can find the max and min values of an array as below.
var arr = [2, 3, 5, 10, 2, -9, 3];
alert("Max value is " + arrayMaxMin(arr, "Max"));
alert("Min value is " + arrayMaxMin(arr, "Min"));
function arrayMaxMin(array, selector) {
var val = array[0]; // variable to hold the current max/min value.
for (var i = 1; i < array.length; i++) {
if (selector == "Min") {
if (array[i] < val) {
val = array[i];
}
} else if (selector == "Max") {
if (array[i] > val) {
val = array[i];
}
}
}
return val;
}
You can use Math functions. Math.min will return you the smallest number of the passed numbers. You cannot call these functions directly on array so, you can use apply to call these functions and pass the array as parameters to the functions.
The Math.min() function returns the smallest of zero or more numbers.
Math.max as follow:
// array: The source array
// type: 'max' or 'min' to find Maximum or Minimum number
var getNum = function(array, type) {
return Math[type].apply(null, array);
};
var arr = [1, 3, 35, 12];
document.write('Max: ' + getNum(arr, 'max'));
document.write('<br />Min: ' + getNum(arr, 'min'));
UPDATE
they does not allow us use min max function
You can use array methods to get maximum and minimum value. Sort the array in ascending order and then the first element in array will be minimum and last element is maximum.
// array: Source array
// type: 'max' or 'min'
var getNum = function(array, type) {
// Sort the array by ascending order
array.sort(function(a, b) {
return a < b;
});
return type === 'max' ? array[0] : array[array.length - 1];
};
var vector = [233, 10, 32543, 54321];
var max = getNum(vector, 'max');
var min = getNum(vector, 'min');
document.write('Max: ' + max);
document.write('<br />Min: ' + min);
This should work too!. Hope it helps bro.
var arr = [2 ,4 ,56, 23, 10];
var max = arr.reduce(function(x,y){
return (x > y) ? x : y;
});
var min = arr.reduce(function(x,y){
return (x < y) ? x : y;
});
console.log('Max: '+ max);
console.log('Min: '+ min);
You could do something like this:
function extremeValue(my_arr, min_max) {
var retval, i, len;
for(i=0,len=my_arr.length; i < len; i++) {
if (min_max == 'minimum') {
retval = (my_arr[i] < retval ? my_arr[i] : retval);
} else {
retval = (my_arr[i] > retval ? my_arr[i] : retval);
}
}
return retval;
}
Then you would call it like this:
console.log(extremeValue(vector, 'maximum'));
console.log(extremeValue(vector, 'minimum'));
Without the Built in Math Min/Max method:
let minMaxValues = (arr) => {
let maxValues;
let minValues;
for (let i = 0; i < arr.length; i++) {
// check first value with all iterater
if(arr[i] < arr[1]){
minValues = arr[i];
}
// check last value with all iterater
if(arr[i] > arr[arr.length-1]){
maxValues = arr[i];
}
}
console.log(minValues);
console.log(maxValues);
}
minMaxValues([100, 20, 30, 10, 50]);