I'm trying to write a function that finds the two biggest value inside an array of numbers and stores them inside a new array. I'm unable to first remove the first biggest number from the original array and then find the second biggest.
here is my code:
function choseBig (myArray) {
//var myArray = str.split(" ");
var result = [];
var firstBig;
var secondBig;
// select the biggest value
firstBig = Math.max.apply(Math, myArray);
// find its index
var index = myArray.indexOf(firstBig);
// remove the biggest value from the original array
var myArray_2 = myArray.slice((index -1), 1);
// choose the second biggest value
secondBig = Math.max.apply(Math, myArray_2);
// push the results into a new array
result.push(firstBig, secondBig);
return result;
}
console.log(choseBig ([1,2,3,4,5,9]));
At first glance, I'd suggest:
function choseBig(myArray) {
return myArray.sort((a, b) => b - a).slice(0, 2);
}
console.log(choseBig([1, 2, 3, 4, 5, 9]));
To extend the above a little, for example offering the user the option to specify whether the returned values should be the highest numbers, or the lowest numbers, and how many they wish returned, I'd offer the following:
function choseBig(myArray, opts) {
// 'max': Boolean,
// true: returns the highest numbers,
// false: returns the lowest numbers
// 'howMany': Number,
// specifies how many numbers to return:
var settings = {
'max': true,
'howMany': 2
};
// ensuring we have an Object, otherwise
// Object.keys( opts ) returns an error:
opts = opts || {};
// retrieving the keys of the opts Object, and
// uses Array.prototype.forEach() to iterate over
// those keys; 'o' (in the anonymous function) is
// the array element (the property-name/key) from
// the array Object keys over which we're iterating:
Object.keys(opts).forEach(function(o) {
// updating the settings Object to the new values
// (if any is specified) to those set in the user-
// supplied opts Object:
settings[o] = opts[o];
});
// here we first sort the Array, using a numeric sort;
// using ES2015 Arrow functions. 'a' and 'b' are supplied
// by Array.prototype.sort() and refer to the current ('a')
// and next ('b') array-elements. If b - a is less than zero
// b is moved to a lower index; if a - b is less than zero
// a is moved to a lower index.
// Here we use a ternary operator based on whether settings.max
// is true; if it is true we sort to move the larger number to
// the lower index; otherwise we sort to move the smaller number
// to the lower index.
// Then we slice the resulting array to return the numbers from
// the 0 index (the first number) to the settings.howMany number
// (the required length of the array).
// this is then returned to the calling context.
return myArray.sort((a, b) => settings.max === true ? b - a : a - b).slice(0, settings.howMany);
}
console.log(choseBig([1, 2, 3, 4, 5, 9], {
// here we specify to select the largest numbers:
'max': true,
// we specify we want the 'top' three numbers:
'howMany': 3
}));
function choseBig(myArray, opts) {
var settings = {
'max': true,
'howMany': 2
};
opts = opts || {};
Object.keys(opts).forEach(function(o) {
settings[o] = opts[o];
});
return myArray.sort((a, b) => settings.max === true ? b - a : a - b).slice(0, settings.howMany);
}
console.log(choseBig([1, 2, 3, 4, 5, 9], {
'max': true,
'howMany': 3
}));
JS Fiddle demo.
References:
Array.prototype.forEach.
Array.prototype.slice().
Array.prototype.sort().
Conditional (Ternary) Operator: statement ? ifTrue : ifFalse
How do you use the ? : (conditional) operator in JavaScript?.
Object.keys().
Why not just sort it (descending order) and take the first two entries
biggest = myArray.sort(function(a,b){return b - a}).slice(0,2);
The answers above are probably better and more compact, but in case you don't want to use sort() this is another option
function choseBig (myArray) {
var result = [], firstBig, secondBig;
// select the biggest value
firstBig = Math.max.apply(Math, myArray);
// find its index
var index = myArray.indexOf(firstBig);
// remove the biggest value from the original array
myArray.splice((index), 1);
secondBig = Math.max.apply(Math, myArray);
// push the results into a new array
result.push(firstBig, secondBig);
return result;
}
console.log(choseBig ([1,2,3,4,5,9]));
A linear solution with Array#reduce without sorting.
var array = [1, 2, 3, 4, 5, 9],
biggest = array.reduce(function (r, a) {
if (a > r[1]) {
return [r[1], a];
}
if (a > r[0]) {
return [a, r[1]];
}
return r;
}, [-Number.MAX_VALUE, -Number.MAX_VALUE]);
document.write('<pre>' + JSON.stringify(biggest, 0, 4) + '</pre>');
Edit: more than one biggest value
var array = [1, 2, 3, 4, 5, 9, 9],
biggest = array.reduce(function (r, a) {
if (a > r[1]) {
return [r[1], a];
}
if (a > r[0]) {
return [a, r[1]];
}
return r;
}, [-Number.MAX_VALUE, -Number.MAX_VALUE]);
document.write('<pre>' + JSON.stringify(biggest, 0, 4) + '</pre>');
With Math#max and Array#splice
var first = Math.max(...arr)
arr.splice(arr.indexOf(first))
var second = Math.max(...arr)
and with ES6 spread operator
I like the linear solution of Nina Scholz. And here's another version.
function chooseBig (myArray) {
var a = myArray[0], b = myArray[0];
for(i = 1; i < myArray.length; i++) {
if (myArray[i] === a) {
continue;
} else if (myArray[i] > a) {
b = a;
a = myArray[i];
} else if (myArray[i] > b || a === b) {
b= myArray[i];
}
}
return [a, b];
}
If you want to retrieve the largest two values from a numeric array in a non-destructive fashion (e.g. not changing the original array) and you want to make it extensible so you can ask for the N largest and have them returned in order, you can do this:
function getLargestN(array, n) {
return array.slice(0).sort(function(a, b) {return b - a;}).slice(0, n);
}
And, here's a working snippet with some test data:
function getLargestN(array, n) {
return array.slice(0).sort(function(a, b) {return b - a;}).slice(0, n);
}
// run test data
var testData = [
[5,1,2,3,4,9], 2,
[1,101,22,202,33,303,44,404], 4,
[9,8,7,6,5], 2
];
for (var i = 0; i < testData.length; i+=2) {
if (i !== 0) {
log('<hr style="width: 50%; margin-left: 0;">');
}
log("input: ", testData[i], " :", testData[i+1]);
log("output: ", getLargestN(testData[i], testData[i+1]));
}
<script src="http://files.the-friend-family.com/log.js"></script>
[1,101,22,202].sort(function(a, b){return b-a})[0]
[1,101,22,202].sort(function(a, b){return b-a})[1]
Related
I was making a JavaScript program that would check if array contains consecutive values like 1,2,3,4,5 or 5,4,3,2,1. Now, I've been successful in checking if it contains numbers in order like 1,2,3,4,5 but for some reason I just can't seem to make it work for checking in reverse order (like 5,4,3,2,1)
Here's my code:
let nums = prompt("Enter number separated by hyphen");
let numcol = nums.split("-");
let intnumcol = [];
for (let i = 0; i < numcol.length; i++) {
intnumcol.push(parseInt(numcol[i]));
}
for (let j = 0; j < intnumcol.length; j++) {
if (j > 0) {
if (intnumcol[j] === ++intnumcol[j - 1] || intnumcol[j]===--intnumcol[j-1] ) {
let flag = true;
if (!flag)
break;
if (j == intnumcol.length - 1 && flag == true) {
console.log("consecutive");
break;
}
} else {
console.log("not consecutive")
break;
}
}
}
Here, in this statement:
if (intnumcol[j] === ++intnumcol[j - 1] || intnumcol[j]===--intnumcol[j-1] )
I do have the OR condition for checking reversed order values but for some reason, it is not working. I don't know why. So please help me.
Here's the JS Fiddle link : https://jsfiddle.net/Lrwagc3m/
Also, please don't confuse this to be the duplicate of some other question because my questions asks for why this certain code doesn't work for checking reverse order consecutive values and for knowing the code to make it work.
I have put together some functions to help you determine if an array contains only numbers in ascending or descending order. The most important is the consecutiveChecker which receives the array and a function that compares 2 consecutive elements. If there are any 2 consecutive elements which don't respect this condition, the function will return false. If we reached the end of array, it will return true.
As you can see, I have 2 filters prepared, one that checks ascending order and another one for checking descending order, which are later used in consecutiveAscOrDescChecker, for which I've written some test cases and some code similar to yours to prompt the user for some numbers and return back the answer.
const ascendingOrder = (a, b) => a - b === -1;
const descendingOrder = (a, b) => ascendingOrder(b, a);
const consecutiveChecker = (array, consecutiveFilter) => {
for (let i = 1; i < array.length; i++) {
if (!consecutiveFilter(array[i - 1], array[i])) {
return false;
}
}
return true;
}
const consecutiveAscOrDescChecker = (array) =>
consecutiveChecker(array, ascendingOrder) ||
consecutiveChecker(array, descendingOrder);
const testCases = [
[1, 2, 3, 4, 5], // true
[1, 2, 3, 4, 6], // false
[5, 4, 3, 2, 1], // true
[-1, 0, 1], // true
[1, 0, -1], // true
[1, 2, 3, 3, 4], // false
[1], // true
[] // true
];
for (const array of testCases) {
console.log(array, consecutiveAscOrDescChecker(array));
}
const numbers = prompt("Enter numbers separed by '-'")
.split("-")
.map(x => parseInt(x));
alert(`${numbers} array is ${consecutiveAscOrDescChecker(numbers) ? 'consecutive' : 'not consecutive'}`);
Note: this setup let's you check other things easily, e.g. what if you wanted to check if consecutive elements grow by 2? Easy:
const growByTwo = consecutiveChecker(array, (a, b) => a - b === -2);
Wanna check if an array elements grows/shrinks by a particular number? Easy:
const consecutiveBy = (x) => {
const ascendingByX = (a, b) => a - b === -x;
const descendingByX = (a, b) => ascendingByX(b, a);
return (array) =>
consecutiveChecker(array, ascendingByX) ||
consecutiveChecker(array, descendingByX);
}
const consecutiveByThree = consecutiveBy(3);
console.log(consecutiveByThree([1, 3, 5])); // false
console.log(consecutiveByThree([1, 4, 7])); // true
With this function, consecutiveAscOrDescChecker would be as easy as:
const consecutiveAscOrDescChecker = consecutiveBy(1);
console.log(consecutiveAscOrDescChecker([1, 2, 4])); // false
console.log(consecutiveAscOrDescChecker([1, 2, 3])); // true
Note that you can't use Math.abs with consecutiveChecker as it would give you a different outcome, e.g.
consecutiveChecker([1, 2, 1, 0, -1, 0], (a, b) => Math.abs(a - b) === 1); would return true(!!) because the difference between any 2 consecutive elements, ascending or descending, is 1, which is a different problem than yours.
you can try something like the below which will help you to find out whatever input was provided in ascending or descending order
AscendingOrDescending = (inputString) => {
const arrayOfNumbers= Array.from(inputString);
const asscendingResult = arrayOfNumbers.every((number,index,array) => {
return index < array.length ? Number(number) + 1 === Number(array[index+1]) || Number(number) - 1 === Number(array[index-1]) : true;
});
return asscendingResult ? 'string is in ascending or descending order' : 'string is not in ascending or descending order'
}
console.log(AscendingOrDescending("123456"))
I'm trying to solve this task of finding the unique element inside an array.
So far I managed to solve 95%, but I'm failing on 0. I get an error saying that expected 0 and got 1.
I should get //10, which it does, but after I'm failing the test online. For all other values it has passed.
Any ideas about how to solve this and what I'm missing here?
function findOne(arr) {
let x = arr[0];
for (let i of arr) {
if (i === x) {
continue;
} else {
x = i;
}
return x;
}
}
console.log(findOne([3, 10, 3, 3, 3]));
I don't really understand your code. You start with the first value in the array, then you loop through the array, skipping anything that's the same, and then return the first one that's not the same. That won't find unique values, it'll just find the first value that doesn't equal the first value. So for instance, try it on the array [1,2,2,2,2] and you'll get a result of 2 instead of 1, even though that's clearly wrong.
Instead, you can create a map of each value and its incidence, then filter by the ones that equal 1 at the end.
function findOne(arr) {
const incidences = arr.reduce((map, val) => {
map[val] = (map[val] || 0) + 1;
return map;
}, {});
const values = Object.keys(incidences);
for (let i = 0; i < values.length; ++i) {
if (incidences[values[i]] === 1) { return values[i]; }
}
return null;
}
EDIT The above won't preserve the type of the value (i.e. it'll convert it to a string always, even if it was originally a number). To preserve the type, you can use an actual Map instead of an object:
function findOne(arr) {
const incidences = arr.reduce((map, val) => {
map.set(val, (map.get(val) || 0) + 1);
return map;
}, new Map());
const singletons = Array.from(incidences).filter(entry => entry[1] === 1);
return singletons.map(singleton => singleton[0]);
}
Consider the following:
Recall that a span = max - min + 1;
Let Partition P1 be span from 0..span-1;
Let Partition P2 be span from span..(2*span)-1:
Place a number in P1 if it is not in P2.
Place a number in P2 if it is already in P1.
Once the number is in P2, do not consider it again.
If a number is in P1 then it is unique.
You can get all values that appear once, by using a map to count how many times each element has appeared. You can then reduce that map into an array of unique values:
const findUnique = arr => {
const mapEntries = [...arr.reduce((a, v) => a.set(v, (a.get(v) || 0) + 1), new Map()).entries()]
return mapEntries.reduce((a, v) => (v[1] === 1 && a.push(v[0]), a), [])
}
console.log(findUnique([3, 10, 3, 3, 3]))
console.log(findUnique([1, 2, 3, 2, 4]))
console.log(findUnique([4, 10, 4, 5, 3]))
If you don't care about multiple unique values, you can just sort the array and use logic, rather than checking every value, provided the array only contains 2 different values, and has a length greater than 2:
const findUnique = arr => {
a = arr.sort((a, b) => a - b)
if (arr.length < 3 || new Set(a).size === 1) return null
return a[0] === a[1] ? a[a.length-1] : a[0]
}
console.log(findUnique([3, 10, 3, 3, 3]))
console.log(findUnique([3, 3, 1]))
console.log(findUnique([3, 1]))
console.log(findUnique([3, 3, 3, 3, 3]))
Your code is complex, Try this
function findOne(arr) {
const uniqueItems = [];
arr.forEach(item => {
const sameItems = arr.filter(x => x === item);
if (sameItems.length === 1) {
uniqueItems.push(item);
}
});
return uniqueItems;
}
console.log(findOne([0, 1, 1, 3, 3, 3, 4]));
I'm getting all unique items from passed array, It may have multiple unique item
this is a way simpler and fast:
function findOne(arr) {
const a = arr.reduce((acc, e) => {
e in acc || (acc[e] = 0)
acc[e]++
return acc
}, {})
return Object.keys(a).filter(k => a[k] === 1)[0] || null
}
I have an array with numbers whose absolute value is guaranteed to be less than 10.
What I'm doing now is sorting it in ascending order using Array.prototype.sort():
myArray.sort(function (a, b) {
return a - b;
})
But the task is to sort by groups without repeats, in other words having an array
a = [1,2,2,3,1,4,4,2,9,8]
I need to get the output
b = [1,2,3,4,8,9,1,2,4]
I had an idea to use Array.prototype.push() inside functional expression to add duplicate numbers to the end of the array. But for obvious reasons I can't do so because of the existence of a scope:
myArray.sort(function (a, b) {
if(a === b){
this.myArray.push(b);
return 0;
}
else{
return a - b;
}
})
Is it possible to implement my idea using Array.prototype.sort() or is it easier and more correct to write a separate function?
You could use sorting with map by using a temporary object with a hashtable for the same group array. Take from it the length of the used array as group for sorting.
The sorting happens with group and value.
The result is mapped with index of the sorted temporary array.
var array = [1,2,2,3,1,4,4,2,9,8],
groups = Object.create(null),
result = array
.map((value, index) => ({ index, value, group: groups[value] = (groups[value] || 0 ) + 1 }))
.sort((a, b) => a.group - b.group || a.value - b.value)
.map(({ value }) => value);
console.log(...result);
Below is on approach you can take - the comments have details of what each step is for:
const a = [1, 2, 2, 3, 1, 4, 4, 2, 9, 8];
//Create an occurrence map
const map = a.reduce((accum, i) => {
if (accum[i]) {
accum[i] += 1;
} else {
accum[i] = 1;
}
return accum;
}, {});
//We need to iterate the map as many times as the largest value
const iterations = Math.max(...Object.values(map));
const sorted = [];
for (let i = 0; i < iterations; i += 1) {
Object.entries(map).forEach(entry => {
const [val, count] = entry;
if (count > 0) {
sorted.push(parseInt(val)); //Add it to our sorted array
map[val] -= 1; //Reduce the number of occurrences in the map for this key
}
});
}
console.log(sorted);
You could create a group object which creates each number as key and an array of the number as value. Then, loop through the object and add each number to output. Every time the array becomes empty, delete the key. Run this until the object has no keys left.
const input = [1, 2, 2, 3, 1, 4, 4, 2, 9, 8],
group = {},
output = [];
input.forEach(n => (group[n] = group[n] || []).push(n))
while (Object.keys(group).length > 0) {
for (const key in group) {
output.push(group[key].pop())
if (group[key].length === 0)
delete group[key];
}
}
console.log(output)
(Note: For numerical keys, the keys of an object are traversed in the ascending order. So, this only works if there are natural numbers in the array)
I am creating a function that takes in an array of unsorted integers and returns an array of the unique integers, sorted by frequency. However, if the integers have the same frequency, they will be returned in the original order of the input array. Here is my current function:
function uniqueUnionSorted(arr) {
counter = {};
for(var i=0; i<arr.length; i++) {
if (arr[i] in counter) {
counter[arr[i]] ++;
} else {
counter[arr[i]] = 1;
}
}
sortedStrings = Object.keys(counter).sort(function(a,b) {
return counter[b] - counter[a]
});
var sortedNumbers = sortedStrings.map(Number);
return sortedNumbers;
}
So for an array like this:
arr = [1, 3, 2, 1, 5, 2, 1, 4]
the function should return:
[1,2,3,5,4]
However, my function is sorting the 5 and 4 and is returning:
[1,2,3,4,5]
Please help!
The cause of this reordering is that object properties that are numerical will come out ordered when using Object.keys().
Instead of defining counter as an object, use a Map, which will retain the insertion order:
function uniqueUnionSorted(arr) {
var counter = new Map();
for(var i=0; i<arr.length; i++) {
counter.set(arr[i], (counter.get(arr[i]) || 0) + 1);
}
// Spreading the Map will produce an array of pairs
var sortedNumbers = [...counter].sort(function(a,b) {
return b[1] - a[1]; // sort by count
}).map(a => a[0]); // only keep the values, not the counts
return sortedNumbers; // Map keys retain original type, so they remain numeric
}
arr = [1, 3, 2, 1, 5, 2, 1, 4]
console.log(uniqueUnionSorted(arr));
In counter object we can also save lowest index of each element so we can keep elements with lower index earlier in sorted array.
function uniqueUnionSorted(arr) {
counter = {};
for(var i=0; i<arr.length; i++) {
if (arr[i] in counter) {
counter[arr[i]].count ++;
} else {
counter[arr[i]] = {'count' : 1, 'index' : i}; //save lowest index of element
}
}
sortedStrings = Object.keys(counter).sort(function(a,b) {
return counter[b].count - counter[a].count || counter[a].index - counter[b].index;
});
var sortedNumbers = sortedStrings.map(Number);
return sortedNumbers;
}
console.log(uniqueUnionSorted([1, 3, 2, 1, 5, 2, 1, 4]));
https://jsfiddle.net/anLrwwfa/4/
Here's another way you could do this using a Set object, an object to store frequency, and the original array to keep the origin intact. It's a bit longer than the current popular answer but I was in the midst of writing it, so I figured I would throw my hat in the ring.
function sortArr(arr) {
let uniqueValues = new Set();
let frequencies = new Object();
//add values to set object
arr.map((val) => uniqueValues.add(val));
//get frequencies of values
for (let val of uniqueValues) {
frequencies[val] = 0;
}
arr.map((val) => frequencies[val]++);
//sort by frequency, then sort by original placement in array
let sorted_arr = Array.from(uniqueValues).sort((a, b) => {
return frequencies[a] - frequencies[b];
}).sort((a, b) => {
return (frequencies[a] === frequencies[b]) ?
Array.from(uniqueValues).indexOf(a) - Array.from(uniqueValues).indexOf(b) :
b;
});
//return array
return sorted_arr;
};
sortArr([1, 3, 2, 1, 5, 2, 1, 4]); //1,2,3,5,4
EDIT
optimized code a bit
May add an additional condition to actually sort after the original array index if the frequency is equal:
sortedStrings = Object.keys(counter).sort(function(a,b) {
return counter[b] - counter[a] || arr.indexOf(+a)-arr.indexOf(+b)
});
http://jsbin.com/zewulanozo/edit?console
Looking for a way to solve this problem by recursing sum(). Right now, the code works, but I am supposed to call sum() more than once, and it should not mutate the input array.
var sum = function(array) {
if(array.length === 0){
return 0;
}
function add(array, i){
console.log(array[i]);
if(i === array.length-1){
return array[i];
}
return array[i] + add(array, i+1);
}
return add(array, 0);
};
sum([1, 2, 3, 4, 5, 6]) //21
A one-liner that meets all your requirements:
var sum = function(array) {
return (array.length === 0) ? 0 : array[0] + sum(array.slice(1));
}
// or in ES6
var sum = (array) => (array.length === 0) ? 0 : array[0] + sum(array.slice(1));
// Test cases
sum([1,2,3]); // 6
var s = [1,2,3];
sum(s); // 6
sum(s); // 6
Reasoning
In a recursive call, you need to model your task as reduction to a base case. The simplest base case in this case is the empty array - at that point, your function should return zero.
What should the reduction step be? Well you can model a sum of an array as the result of adding the first element to the sum of the remainder of the array - at some point, these successive calls will eventually result in a call to sum([]), the answer to which you already know. That is exactly what the code above does.
array.slice(1) creates a shallow copy of the array starting from the first element onwards, and no mutation ever occurs on the original array. For conciseness, I have used a ternary expression.
Breakdown:
sum([1,2,3])
-> 1 + sum([2,3])
-> 1 + 2 + sum([3])
-> 1 + 2 + 3 + sum([])
-> 1 + 2 + 3 + 0
-> 6
You're on the right track, but consider that sum could take an optional second argument (that defaults to zero) that indicates the position to start summing from...
function sum(array, n) {
n ||= 0;
if (n === array.length) {
return 0;
} else {
return array[n] + sum(array, n + 1);
}
}
function sumNumbersRecursively(input){
if (input.length == 0){
return 0;
} else{
return input.shift() + sumNumbersRecursively(input);
}
}
console.log(sumNumbersRecursively([2,3,4]))
You don't really need the add function inside your sum function just inline the function and initiate with, 0 as a starting point, or optionally check the i variable for undefined and initialize it to 0!
var sum = function(array, i) {
if(array.length === 0){
return 0;
}
console.log(array[i]);
if(i === array.length-1){
return array[i];
}
return array[i] + sum(array, i+1);
};
console.log(sum([1, 2, 3, 4, 5, 6],0)) //21
You have two solutions:
you can use .reduce() method
or perform a simple tail recursion
With reduction:
function sum(a, b) {
return a + b;
}
const array = [1, 2, 3, 4, 5, 6];
//In our reduce, we will apply our sum function,
//and pass the result as the next value
const res = array.reduce(sum);
With recursion:
function sumRec(array, acc = 0, index) {
//We will update our accumulator, and increment
// the value of our current index
return index === array.length
? acc
: sumRec(array, acc += array[index], ++index);
}
console.log(sumRec(array, 0, 0));
Personally, I find the first solution more elegant.
arr = [1,2,3,4]
sum = arr.reduce((acc, curr)=> acc+curr)
function sumArr(arr){
if(arr.length>1){
return arr.pop()+sumArr(arr);
}else{
return arr[0];
}
}
If you have to call sum more than once, then use the binary approach: split the array in half and recur on each piece. When you get to a length of 1, return the single value.
Does this work for you? I'm afraid I don't recall the JS syntax for array slices, so my recursion statement may be wrong in the details.
var sum = function(array) {
if(array.length === 1){
return array[0];
}
mid = array.length / 2
return sum(array[0:mid-1]) + sum(array[mid:array.length-1])
};
sum([1, 2, 3, 4, 5, 6]) //21