Sort an array in a peculiar order - javascript

I've been trying to figure out how to solve this problem but I can't seem to find the proper sorting order.
Instructions:
Write a program that orders a list of numbers in the following way:
3,-2,1,0,-1,0,-2,1 => -2,-1,-2,0,0,3,1,1
'use strict';
let myNumbers = '3,-2,1,0,-1,0,-2,1';
// I receive the input as a string and split and map it into an array
let myNumbers = gets().split(',').map(Number);
I've tried applying the sort() method in ascending order to all integers below zero and doing the opposite for those above but that's not quite the order in the expected output.
I've also attempted to splice the first array after applying sort() to 0 and then re-arrange the spliced part and concatenate it. However, that won't work with all test inputs.
Another example:
3,-12,0,0,13,5,1,0,-2 => -12,-2,0,0,0,3,13,5,1
What is the logic in this order? Thanks.

Because this sounds like the solution to a homework problem or something of the like, I'll let you write the code:) But the way I would do it is in one iteration through the array, create three separate arrays:
Negative numbers
0s
Positive numbers
Squish the arrays together without sorting and you have your O(N) solution.

So based on sketrik answer which covers the logic, this is the code:
const myNumbers = '3,-2,1,0,-1,0,-2,1';
const arr = myNumbers.split(',').map(Number)
const res = arr.filter(i => i < 0)
.concat(arr.filter(i => i === 0))
.concat(arr.filter(i => i > 0))
console.log(res)
This works thanks to two very basic JS methods of Array.prototype:
concat and filter. I could not explain them better than the documentation, check it out!
But basically, what I am doing is:
find the chunk with negatives with arr.filter(i => i < 0)
find the chunk with zeros with arr.filter(i => i === 0)
find the chunk with positives with arr.filter(i => i > 0)
concat them all into one array.

I am Neo.
'use strict';
let myInput = '3,-2,1,0,-1,0,-2,1';
let myNumbers = myInput.split(',').map(Number);
let negatives = [];
let zeroes = [];
let positives = [];
for (const element of myNumbers) {
if (element < 0) {
negatives.push(element);
} else if (element === 0) {
zeroes.push(element);
} else if (element > 0) {
positives.push(element);
}
}
let sortedArr = negatives.concat(zeroes, positives);
console.log(sortedArr.join(','));

Logic or Typo?
"...ascending order to all integers below zero and doing the opposite for those above..."
Following what was posted in question the examples should be:
-2, -2, -1, 0, 0, 3, 1, 1 and -12, -2, 0, 0, 0, 13, 5, 3, 1
Zero and less ascending: -3, -2, -1, 0. Greater than zero descending: 3, 2, 1
To get those results see Demo 1.
Strictly going by the examples is simpler:
-2, -1, -2, 0, 0, 3, 1, 1 and -12, -2, 0, 0, 0, 3, 13, 5, 1
Group negative numbers, then zeros, and then positive numbers: [-][0][+]. No order within the three arrays is required. Order is only required for the three groups.
To get those results see Demo 2.
Explanation
Demo 1
First, sort array in ascending order:
const ordered = array.sort((current, next) => current - next);
Next, find the index of the first number that's greater than 0, then extract all numbers beginning at that index and ending at the last number. Store the extracted array in a variable:
const positive = ordered.splice(ordered.findIndex(number => number > 0));
Finally, sort extracted array in descending order and then concatenate the extracted array to the end of the original array:
return ordered.concat(positive.sort((current, next) => next - current));
Demo 2
Create three new arrays returned by the filter() method: negative (n < 0), zero (n === 0), and positive (n > 0).
Then concatenate them into one array:
const negative = array.filter(number => number < 0);
const zero = array.filter(number => number === 0);
const positive = array.filter(number => number > 0);
return negative.concat(zero, positive);
Demo 1
const unorderedA = [3, -2, 1, 0, -1, 0, -2, 1];
const unorderedB = [3, -12, 0, 0, 13, 5, 1, 0, -2];
const illogical = array => {
const ordered = array.sort((current, next) => current - next);
const positive = ordered.splice(ordered.findIndex(number => number > 0));
return ordered.concat(positive.sort((current, next) => next - current));
};
// For demonstration purposes
const log = data => {
const string = Array.isArray(data) ? `[${data.join(', ')}]` : data;
return console.log(string);
};
log(illogical(unorderedA));
log(illogical(unorderedB));
Demo 2
const unorderedA = [3, -2, 1, 0, -1, 0, -2, 1];
const unorderedB = [3, -12, 0, 0, 13, 5, 1, 0, -2];
const illogical = array => {
const negative = array.filter(number => number < 0);
const zero = array.filter(number => number === 0);
const positive = array.filter(number => number > 0);
return negative.concat(zero, positive);
};
// For demonstration purposes
const log = data => {
const string = Array.isArray(data) ? `[${data.join(', ')}]` : data;
return console.log(string);
};
log(illogical(unorderedA));
log(illogical(unorderedB));

Related

Move Zeros logic break down Javascript

I am working through this solution that I saw in this discussion section of leetcode and am unable to grasp part of the logic. The name of the game is to move all zeros to the end of a given array in place while maintaining the order of the other numbers. The increment operator inside the index of j is where I am lost because wouldn't that place the non-zero number to the right?
var moveZeroes = function(nums) {
let j = 0
for(let i = 0; i < nums.length; i++) {
if(nums[i] !== 0) {
//storing the index we are iterating on
let n = nums[i]
//changing the index in place to 0
nums[i] = 0
//console.log(nums);
//
nums[j++] = n
console.log(nums);
}
}
return nums;
};
console.log(moveZeroes([0,1,0,3,12]));
Remember that j++ is post-increment, so the initial value of j is used to index nums and then j is incremented. If it was ++j then j would be incremented first, then used.
Simply Remove All Zeros. Add Removed Zeros to the end.
var moveZeroes = (nums) =>
(
nums.toString().replaceAll("0,", "") +
",0".repeat(("" + nums).replace(/[^0]/g, "").length)
)
.split(",")
.map(Number);
console.log(moveZeroes([0, 1, 0, 0, 3, 0, 12])); //[ 1, 3, 12, 0, 0, 0, 0 ]
NOTE:
(""+nums).replace(/[^0]/g,'').length: number of 0 in the Array
nums.toString(): To convert array to string we use or we could use the trick of concatenating with an empty string like ""+nums
split(','): To convert string into an array
map(Number): To convert an array of strings to numbers.
Update
OP wants to mutate the array, "Solution A" deals with immutable arrays and "Sulution B" nutates the array.
Solution A (Immuatable)
Try using Array.filter() to separate zeroes (ex. num === 0) and numbers that are not zeroes (ex. num !== 0) into two new and separate arrays. Then use either Array.concat() or the spread operator to merge them into a single array.
// log() is an optional utility function that formats console logs
const log = data => console.log(`[${data}]`);
// This input array has zeroes everywhere
const numbers = [0, 1, 0, 0, 3, 0, 12];
const moveZeroes = array => {
// With the given array called "numbers"
// left = [1, 3, 13]
const left = array.filter(num => num !== 0);
// right = [0, 0, 0, 0]
const right = array.filter(num => num === 0);
// Then arrays "left" and "right" are merged
return [].concat(left, right)
};
log(moveZeroes(numbers));
Solution B (Muatable)
Array.reverse() the array, track the index with Array.entries(), and run it through a for...of loop. If a number isn't a zero, Array.splice() it out then Array.unshift() it to the beginning of the array. Note, .reverse(), .splice(), and .unshift() are mutating Array methods.
// log() is an optional utility function that formats console logs
const log = data => console.log(`[${data}]`);
// This input array has zeroes everywhere
const numbers = [0, 1, 0, 0, 3, 0, 12];
const moveZeroes = array => {
/* .reverse() the array, track index with
.entries(), and loop thru with a for...of
loop */
for (let [idx, num] of array.reverse().entries()) {
/* if a number isn't zero, remove it with
.splice(), then .unshift() it into the
beginning of array */
if (num !== 0) {
array.splice(idx, 1);
array.unshift(num);
}
}
return array;
};
log(moveZeroes(numbers));

return "even" if others numbers are odd and "odd" the others number are even javascript

I have 2 questions, how can I get value instead of value inside array and how can I make this code shorter and declarative.
arr = [16, 4, 11, 20, 2]
arrP = [7, 4, 11, 3, 41]
arrTest = [2, 4, 0, 100, 4, 7, 2602, 36]
function findOutlier(arr) {
const isPair = (num) => num % 2 === 0
countEven = 0
countOdd = 0
arr1 = []
arr2 = []
const result = arr.filter((ele, i) => {
if (isPair(ele)) {
countEven++
arr1.push(ele)
} else {
countOdd++
arr2.push(ele)
}
})
return countEven > countOdd ? arr2 : arr1
}
console.log(findOutlier(arrTest))
Filtering twice may be more readable.
even = arr.filter((x) => x % 2 == 0);
odd = arr.filter((x) => x % 2 == 1);
if (even.length > odd.length) {
return even;
} else {
return odd;
}
If you're looking to do this with one loop, consider using the array reduce method to put each number into an even or odd bucket, and then compare the length of those buckets in your return:
function findOutlier(arr) {
const sorted = arr.reduce((acc, el) => {
acc[el % 2].push(el);
return acc;
},{ 0: [], 1: [] })
return sorted[0].length > sorted[1].length ? sorted[1] : sorted[0];
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(findOutlier(arr));
Note that this does not handle when the arrays are the same length gracefully (right now it'll just return the odd array).
You could take an object with the wanted part for collecting and add a short circuit if one of the types has a count of one and the others have a count greater than one.
const
isPair = num => num % 2 === 0,
findOutlier = array => {
count = { true: [], false: [] };
for (const value of array) {
count[isPair(value)].push(value);
if (count.true.length === 1 && count.false.length > 1) return count.true[0];
if (count.false.length === 1 && count.true.length > 1) return count.false[0];
}
};
console.log(...[[16, 4, 11, 20, 2], [7, 4, 11, 3, 41], [2, 4, 0, 100, 4, 7, 2602, 36]].map(findOutlier));
Here is an solution that selects the even or odd array based on the modulo result.
function findOutlier(integers) {
const even = [], odd = [], modulos = [even, odd];
for (const integer of integers) {
modulos[Math.abs(integer % 2)].push(integer);
}
return even.length > odd.length ? odd : even;
}
console.log(findOutlier([2, 4, 0, 100, 4, 7, 2602, 36]));
You unfortunately do need Math.abs() to handle negative values, because -3 % 2 == -1.
See: JavaScript % (modulo) gives a negative result for negative numbers
However the name findOutlier lets me assume there is only a single outlier within the provided list. If this is the case you can optimize the algorithm.
function findOutlier(integers) {
// With less than 3 integers there can be no outlier.
if (integers.length < 3) return;
const isEven = (integer) => integer % 2 == 0;
const isOdd = (integer) => !isEven(integer);
// Determine the outlire based on the first 3 elements.
// If there are 0 or 1 integers even, the outlire is even.
// if there are 2 or 3 integers even, the outlier is odd.
const outlier = integers.slice(0, 3).filter(isEven).length < 2
? isEven
: isOdd;
return integers.find(outlier);
}
console.log(findOutlier([2, 4, 0, 100, 4, 7, 2602, 36]));
You can do this without creating intermediate arrays by simply comparing each element to its neighbors and returning that element if it is different to both, or undefined if no outliers are found. This returns in the same iteration in which the outlier is first encountered, and returns the value itself and not an array.
function findOutlier(array) {
const
len = array.length,
isEven = (n) => n % 2 === 0;
for (const [i, value] of array.entries()) {
let
prev = array[(i-1+len)%len], // loop around if < 0 (first element)
next = array[(i+1)%len]; // loop around if >= length (last element)
if (isEven(value) !== isEven(prev) && isEven(value) !== isEven(next)) {
return value;
}
}
return undefined;
}
const arrays = [[16, 4, 11, 20, 2], [7, 4, 11, 3, 41], [2, 4, 0, 100, 4, 7, 2602, 36]]
console.log(...arrays.map(findOutlier));
Now that OP clarified the requirements (at least in a comment) this allows a different approach:
function findOutlier(array) {
let odd = undefined, even = undefined;
for (let i of array) {
let isEven = i % 2 == 0;
if (odd !== undefined && even !== undefined)
return isEven ? odd : even;
if (isEven) even = i;
else odd = i;
}
if (odd !== undefined && even !== undefined)
return array[array.length-1];
}
console.log(findOutlier([2,4,6,8,10,5]))
The algorithm will iterate the array, and store the lastest found odd and even numbers, respectively.
If we discovered both an odd and an even number already, with the current number we can decide, which of them is the outlier: If the current number is even, it's at least the second even number we found. Thus, the found odd number must be the outlier. The same applies vice versa if the current number is odd. The special case, if the outlier is the last element of the array, is checked with an additional condition after the loop.
If all numbers are odd or even (ie there is no outlier) this function will return undefined. This algorithm does not throw an error, if the preconditions are not met, ie if there is more than one outlier.

How to get the index of the 5 smallest values in an array?

I am trying to get the indexes of the 5 smallest values in an array.
I've tried the code below, however it gives the error: Math.min.apply(...).indexOf is not a function. I'm just not sure what I can use as an alternative?
var smallest = [];
for (var i = 0, length = distances.length; i < length; i++) {
var min = Math.min.apply(null,distances).indexOf();
if (smallest.length <= 5) {
smallest.push(min);
}
console.log(smallest);
}
Thanks!
You could get the keys, sort them with the values and take the top five.
var indices = [...distances.keys()]
.sort((a, b) => distances[a] - distances[b])
.slice(0, 5);
You can use Object.entries() to get [index, value] pairs that you can now sort by value to get the order.
const distances = [1, 4, 8, 3, 3, 5, 9, 0, 4, 2];
const indices = Object.entries(distances)
.sort(([,a],[,b]) => a - b)
.map(([index]) => +index)
.slice(0, 5)
console.log(indices);
Nina's version is better :)
You could add an index to each element, sort the data in ascending order, then splice the first 5 values:
const data = [1, 4, 8, 3, 3, 5, 9, 0, 4, 2]
const indices = data.map((e,i) => [e, i])
.sort()
.splice(0,5)
.map(([,el]) => el)
console.log(indices)

How to convert number value into array with separate value at array where max value of index at list will be 1

Need some help.
I have example value 4.8 (it's a value from rating 4.8/5)
Now I need to convert it into array where array should be like this
[1, 1, 1, 1, 0.8]
What do I mean? It should take value and make array with 5 elements but each of index should be not more than 1.
Other examples:
2.8 should be [1, 1, 0.8, 0 ,0]
0.5 should be [0.5, 0, 0, 0, 0]
5 should be [1, 1, 1, 1, 1]
3 should be [1, 1, 1, 0, 0]
etc...
let number = 4.8;
You can use Array.from() by setting the length to 5. On each take the minimum between 0 and the max of num - i or 1:
const convert = num => Array.from({ length: 5 },
(_, i) => Math.max(Math.min(num - i, 1), 0)
)
console.log(convert(4.5))
console.log(convert(2.5))
console.log(convert(0.5))
console.log(convert(5))
And if precision is an issue, I've added a safeSubtract function to handle that:
const safeSubtract = (n, i) => {
const [int, dec = ''] = String(n).split('.')
return +`${+int - i}.${dec}`
};
const convert = num => Array.from({ length: 5 },
(_, i) => Math.max(Math.min(safeSubtract(num, i), 1), 0)
)
console.log(convert(4.8))
console.log(convert(2.8))
console.log(convert(0.8))
console.log(convert(5))
Note I handle the rounding issue which converts 0.8 into 0.7999999999999998 in some of the other answers, at least in their first versions
const conv = n => {
let [whole, dec] = ("" + n).split("."); // split the number
let newArr = Array
.from({length: 5}) // create a 5 item long array
.fill(1, 0, whole) // fill with 1s until the whole number
.fill(0, whole, 5); // fill with 0s for the rest
if (dec) newArr[whole] = +("." + dec); // insert the decimals if needed
return newArr;
}
// test:
[2.8, .5, 5, 3, 4.789].map(n => console.log(n,":",...conv(n)))
Get the base integer number and the decimal number. Populate the beginning indexes with 1s for however many whole numbers there are. Then, add the decimal in the next index. You may want to do some checks to make sure the number is less than 5 (your array size);
let arr = [0, 0, 0, 0, 0];
let number = 4.8;
let base = Math.floor(number);
let decimal = number - base;
for(let i=0; i<base; i++) {
arr[i] = 1;
}
arr[base] = decimal;
You could calculate each position and get the rest for the next loop.
function getStars(v) {
return Array.from({ length: 5 }, _ => Math.max(Math.min(v--, 1), 0));
}
console.log(...getStars(0.5));
console.log(...getStars(2));
console.log(...getStars(2.8)); // needs some rounding ...
EDITED: I handle rounding issues, now.
You can try with a numToArray function like the following one:
function numToArray(num) {
let intPart = Math.floor(num)
let result = Array()
for(let i = 0; i < intPart; i++) {
result.push(1)
}
if(num > intPart) {
result.push(Number((num - intPart).toFixed(2)))
}
return result
}
console.log(numToArray(2.8))
console.log(numToArray(4.5))
console.log(numToArray(0.8))
console.log(numToArray(4))
Could do something like this:
let n = 3.8;
let arr = [0, 0, 0, 0, 0];
let i = 0;
for (; n > 0; n--) {
arr[i] = n >= 1 ? 1 : parseFloat(n.toFixed(10)); // Fixing js float problems
i++;
}
console.log(arr);

How do I find the largest negative integer in an array?

let test12 = [-1, -2, -3];
I'm stuck on this one. I used the below on positive integers but I'm not sure what to change for negatives.
let test = [1 , 2, 3];
var largest= 0;
for (i=0; i<=test.length;i++){
if (test[i]>largest) {
largest=test[i];
}
}
console.log(largest);
largest negative integer in an array
Your question can have 3 interpretations:
The negative integer that is farthest from 0
The negative integer that is closest to 0
The largest number in general (you might be struggling if an all negative integer array comes along, because you initialize min to 0)
Just to clarify, smallest is 'farthest from zero'. But here's all three ways :) :
const ints = [-3, -2, -1, 0, 1, 2]
const negativeInts = ints.filter(i => i < 0)
const smallestNegative = Math.min(...negativeInts)
const largestNegative = Math.max(...negativeInts)
const largestOverall = Math.max(...ints)
console.log({smallestNegative, largestNegative, largestOverall}) // -3, -1, 2
Hope this helps. Cheers.
Just initialize largest to -Infinity rather than 0. You also need to iterate over the input array's length, not 0 to largest:
let test12 = [-1, -2, -3];
var largest = -Infinity;
for (i = 0; i < test12.length; i++) {
if (test12[i] > largest) {
var largest = test12[i];
}
}
console.log(largest);
Another method would be to spread into Math.max:
let test12 = [-1, -2, -3];
console.log(
Math.max(...test12)
);
Largest negative would be -244, so you can sort and get the first index.
let arr = [-1, -2, -244, -7],
[largets] = arr.slice().sort((a, b) => a - b);
console.log(largets);
Try this..
var array = [-155,3, 6, 2, 56, 32, 5, -89, -32,115,-150];
array.sort(function(a, b) {
return a - b;
});
console.log(array[0]);
In case you want a programming solution ,like an edit that your program needs to perform even if an all negative array is given, then try this :
let test = [-10, -1, -2, -3];
// just Assign largest to the first element in test.
// the largest int might not be greater than zero,
// but will definitely be larger that any other element in the array.
var largest= test[0];
for (i=0; i<=test.length;i++){
if (test[i]>largest) {
largest=test[i];
}
}
console.log(largest);

Categories