How to .sort based on multiple conditions - javascript

I have an array items like so:
items.sort(function(a,b) {
});
a & b have the following properties:
a.suggested (true, false, undefined)
a.meta.member_count (int .. 1 or more, undefined.
I would like to sort items as follows:
all items that are not suggested. Either false or undefined
member_count, highest # first,
Then show any suggested === true
Is sort the right way to go about this? Any suggestions on how I can make this happen in the sort function?
Thank you

This will sort by the 'suggested' value, putting false/undefined values above true values. After that, it will sort by meta.member_count (highest first).
items.sort(function(a,b) {
if (a.meta.member_count === undefined) { x = 0; } else { x = a.meta.member_count;}
if (b.meta.member_count === undefined) { y = 0; } else { y = b.meta.member_count;}
if (a.suggested !== true) {
if (b.suggested !== true) {
n = x - y;
return n < 0 ? 1 : n > 0 ? -1 : 0;
}
return -1;
} else if (b.suggested === true) {
n = x - y;
return n < 0 ? 1 : n > 0 ? -1 : 0;
} else {
return 1;
}
});
The key in this case lies in converting the undefined member_count values to 0 so we can do math with them.
Also, when sorting numbers using the JavaScript sort() function, make sure to do math on the actual numbers and then use an if statement to return 1, -1, or 0. If you return the difference between the values directly, it will compare the values as strings instead of numbers.
Fiddle

Related

Codwars: Array to single value +1

Given an array of integers of any length, return an array that has 1 added to the value represented by the array.
the array can't be empty
only non-negative, single digit integers are allowed
Return nil (or your language's equivalent) for invalid inputs.
Examples
For example the array [2, 3, 9] equals 239, adding one would return the array [2, 4, 0].
My code so far:
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return arr.some(e => typeof e !== 'number' || e < 0) ?
null : i
.toString()
.split('')
.map(e => parseInt(e));
};
It seems to pass most basic test however fails with larger inputs. Where have I gone wrong?
Just like you have converted the array into a number, you have to convert the number back into an array.
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return i.toString().split('').map(x => parseInt(x));
};
console.log(upArray([2,3,9]));
Your code won't work if the array length is greater than 100k...
Number type of javascript or any language is not enough big to handle it.
It's better if we calculate the last element with 1. If result is larger than nice ( < 10 ),
we continue to calculate next element with 1 and assign current value to 0. If result is smaller or equal 9, just assign the result to current and exit loop.
Then we print the final array as result:
pseudo code:
for i from: n-1:0
result = arr[i] + 1;
if(result < 10) :
arr[i] = result;
exit loop;// no need to continue calculate
else:
arr[i] = 0;
endif;
endfor;
You can join final array as string.
Here's probably the fastest solution (performance wise) - also there's no need to deal with BigInt, NaN, or Infinity:
function upArray(arr) {
if (!isInputIsNonEmptyArray(arr)) {
return null;
}
const isNumber = num => typeof num === 'number';
const isIntSingleDigit = num => Number.isInteger(num) && num >= 0 && num <10;
let resultArr = [];
let i = arr.length;
let num;
while (i-- > 0) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
if (num === 9) {
resultArr[i] = 0;
if (i === 0) { //means we're in the msb/left most digit, so we need to insert 1 to the left
resultArr.unshift(1);
break; //you can leave it out really, as the next check in the while will fail anyway
}
}
else {
resultArr[i] = num + 1; //No more + 1 should be made, just check for validity
//of the rest of the input and copy to the result arr
while (--i > -1) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
resultArr[i] = arr[i];
}
break;
}
}
return resultArr;
function isInputIsNonEmptyArray(arr) {
return Array.isArray(arr) && arr.length > 0;
}
}
If the input arg is not an array or an empty array, or if you encounter invalid element during the main while loop you return null.
In the main while loop you go from the right most element (lsd), and add 1 to it (or insert 0 if the number is 9) up the the left most digit.
If a number which is less than 9 is incremented, no need to increment any more (this is the while loop in the else clause).

How to get the max occurrences of a number in an array

as answer to an exercise in which I had to create a function that given an array of numbers return the number with most occurrences, and if more than one number had the max number of occurrences return the minor one. This is the implementation I made, but I'm pulling my hair figuring out why it return 10 instead of 9 in the example.
It appears to be evaluating 10 < 9 as true. What's wrong?
function maxOccurencies(arr) {
var aux = [], max = 0, final = null;
for (var i=0,t=arr.length; i<t; i++) {
aux[arr[i]] = (aux[arr[i]] || 0) + 1;
if (aux[arr[i]] > max) max = aux[arr[i]];
}
for (x in aux) {
if ( aux[x] == max && (x < final || final == null)) {
final = x;
}
}
return final;
}
document.write(maxOccurencies([10,10,10,9,9,9,8,7,4,5,1]));
Putting typeof(x) in your second loop reveals that some of your variables are being cast as type string! Still looking into exactly where this is occurring. You can replace
if ( aux[x] == max && (x < final || final == null)) {
with
if ( aux[x] == max && (parseInt(x) < parseInt(final) || final == null)) {
to return the correct value of 9.
Edit:
Very interesting, I was unaware of Javascript's exact handling of arrays in for...in loops. See the following other questions for more information:
JavaScript For-each/For-in loop changing element types
Why is using “for…in” with array iteration such a bad idea?
Also note that you can use arr.forEach(function(element){...}); and the elements are returned with their types intact.
I think the problem is just that the x in aux is not a number so the if statement isn't evaluating correctly. when converted to a number then it returns 9 (below).
(3 == 3 && ("10" < "9" || "9" == null)) evaluates to true
function maxOccurencies(arr) {
var aux = [], max = 0, final = null;
for (var i=0,t=arr.length; i<t; i++) {
aux[arr[i]] = (aux[arr[i]] || 0) + 1;
if (aux[arr[i]] > max) max = aux[arr[i]];
}
for (x in aux) {
if ( aux[x] == max && (parseInt(x) < final || final == null)) {
final = parseInt(x);
}
}
return final;
}
document.write(maxOccurencies([10,10,10,9,9,9,8,7,4,5,1]));
"I'm pulling my hair figuring out why it return 10 instead of 9 in the example."
That's because in this sort of comparison, 10 is smaller than 9,8,7,6,5,4,3, 2 but a bit grater than 1.
:)
This small type correction will fix it:
function maxOccurences(arr) {
aux = [], max = 0, final = null;
for (var i=0,t=arr.length; i<t; i++) {
aux[arr[i]] = (aux[arr[i]] || 0) + 1;
if (aux[arr[i]] > max) max = aux[arr[i]];
}
for (x in aux) {
if ( aux[x] == max && (+x < final || final == null)) {
final = x;
}
}
return final;
}

Big Integers in Memoized Recursive Fibonacci (Y)

I've written a version of Y that automatically caches old values in a closure using memoization.
var Y = function (f, cache) {
cache = cache || {};
return function (x) {
if (x in cache) return cache[x];
var result = f(function (n) {
return Y(f, cache)(n);
})(x);
return cache[x] = result;
};
};
Now, when almostFibonacci (defined below) is passed into the above function, it returns the value of a large Fibonacci number comfortably.
var almostFibonacci = function (f) {
return function (n) {
return n === '0' || n === '1' ? n : f(n - 1) + f(n - 2);
};
};
However, after a certain value (Number.MAX_SAFE_INTEGER), integers in JavaScript (owing to their IEEE-754 double precision format) are not accurate. So, considering the fact that the only mathematical operations in the Fibonacci function above are addition and subtraction and since operators cannot be overloaded in JavaScript, I wrote naïve implementations of the sum and difference functions (that both use strings to support big integers) which are as follows.
String.prototype.reverse = function () {
return this.split('').reverse().join('');
};
var difference = function (first, second) {
first = first.reverse();
second = second.reverse();
var firstDigit,
secondDigit,
differenceDigits = [],
differenceDigit,
carry = 0,
index = 0;
while (index < first.length || index < second.length || carry !== 0) {
firstDigit = index < first.length ? parseInt(first[index], 10) : 0;
secondDigit = index < second.length ? parseInt(second[index], 10) : 0;
differenceDigit = firstDigit - secondDigit - carry;
differenceDigits.push((differenceDigit + (differenceDigit < 0 ? 10 : 0)).toString());
carry = differenceDigit < 0 ? 1 : 0;
index++;
}
differenceDigits.reverse();
while (differenceDigits[0] === '0') differenceDigits.shift();
return differenceDigits.join('');
};
var sum = function (first, second) {
first = first.reverse();
second = second.reverse();
var firstDigit,
secondDigit,
sumDigits = [],
sumDigit,
carry = 0,
index = 0;
while (index < first.length || index < second.length || carry !== 0) {
firstDigit = index < first.length ? parseInt(first[index], 10) : 0;
secondDigit = index < second.length ? parseInt(second[index], 10) : 0;
sumDigit = firstDigit + secondDigit + carry;
sumDigits.push((sumDigit % 10).toString());
carry = sumDigit > 9 ? 1 : 0;
index++;
}
sumDigits.reverse();
while (sumDigits[0] === '0') sumDigits.shift();
return sumDigits.join('');
};
Now, by themselves, both these functions work perfectly.1
I have now updated the almostFibonacci function to as follows to use the sum function instead of + and the difference function instead of the - operator.
var almostFibonacci = function (f) {
return function (n) {
return n === '0' || n === '1' ? n : sum(f(difference(n, '1')), f(difference(n, '2')));
};
};
As you may have guessed, this does work. It crashes the fiddle in case of even a small number like 10.
Question: What could be wrong? All the functions here work perfectly individually. But in tandem, they seem to fail. Can anyone here help me debug this particularly complex scenario?
1Except an edge case for the difference function. It requires the first argument to be larger than the second.
Now, by themselves, both these functions work perfectly - Except an edge case for the difference function. It requires the first argument to be larger than the second.
And that's the problem. In your fibonacci algorithm you're at some point calculating difference("2", "2"), which needs to yield "0" to work. It does however return the empty string "", which is not tested against as your guard condition for the recursion. When in the next step computing difference("", "1"), the function will fall into an infinite loop.
Solutions:
Fix that edge case (you still won't need to cope with negative numbers)
Don't use strings for the ordinal number, but only for the fibonacci number itself. You hardly will try the compute the (253+1)th fibonacci number, will you? I would assume this to be a significant speed improvement as well.
var fibonacci = Y(function(fib) {
return function(n) {
if (n == 0) return "0";
if (n == 1) return "1";
return sum(fib(n-1), fib(n-2));
};
});
Here is how I solved the problem at hand.
Changes:
I removed the while (differenceDigits[0] === '0') differenceDigits.shift(); statement. Even though this outputs differences without truncated leading zeros, it outputs a '0' in case of an edge case like difference('2', '2').
I edited the return statement in the almostFibonacci function to return n == 0 || n == 1 ? n : sum(f(difference(n, '1')), f(difference(n, '2')));. Notice that I'm checking for 0 and not '0' with a non strict equality operator.1
1The reason I'm doing n == 0 as opposed to n === '0' is because in JavaScript, '00000' == 0 but '00000' !== '0' and in my new updated difference function, without truncated leading zeros, I can't guarantee the number of zeros for a zero output. Well, actually I can. There would be as many zeros as the length of n.
100th Fibonacci - JSFiddle

How can one compare string and numeric values (respecting negative values, with null always last)?

I'm trying to sort an array of values that can be a mixture of numeric or string values (e.g. [10,"20",null,"1","bar","-2",-3,null,5,"foo"]). How can I sort this array such that
null values are always placed last (regardless of sorting order, see jsFiddle)
negative numbers are sorted correctly (i.e. they are less than positive numbers and sort correctly amongst themselves)
? I made a jsFiddle with detailed numeric and string examples (using localeCompare and the numeric option), but will paste the numeric version of my sorting algorithm below as a starting point.
// Sorting order
var order = "asc"; // Try switching between "asc" and "dsc"
// Dummy arrays
var numericArr = [10,20,null,1,-2,-3,null,5];
// Sort arrays
$(".output1").append(numericArr.toString());
numericArr.sort(sortByDataNumeric);
$(".output2").append(numericArr.toString());
// Numeric sorting function
function sortByDataNumeric(a, b, _order) {
// Replace internal parameters if not used
if (_order == null) _order = order;
// If values are null, place them at the end
var dflt = (_order == "asc" ? Number.MAX_VALUE : -Number.MAX_VALUE);
// Numeric values
var aVal = (a == null ? dflt : a);
var bVal = (b == null ? dflt : b);
return _order == "asc" ? (aVal - bVal) : (bVal - aVal);
}
The problem with my string sorting algorithm (see jsFiddle) is that I can't find a way to always place null values last and negative values aren't correctly sorted within themselves (e.g. -3 should be less than -2)
Edit
To answer the comments, I expect [10,"20",null,"1","bar","-2",-3,null,5,"foo"] to sort to [-3,"-2","1",5,10,"20","bar","foo",null,null]
You should first check to see if either value is null and return the opposite value.
On a side note:
For your default _order value, you should check if the parameter is undefined instead of comparing its value to null. If you try to compare something that is undefined directly you will get a reference error:
(undefinedVar == null) // ReferenceError: undefinedVar is not defined
Instead, you should check if the variable is undefined:
(typeof undefinedVar == "undefined") // true
Also, it's probably a better idea to wrap your compare function in a closure instead of relying on a global order variable.
Sometime like:
[].sort(function(a, b){ return sort(a, b, order)})
This way you can sort at a per-instance level.
http://jsfiddle.net/gxFGN/10/
JavaScript
function sort(a, b, asc) {
var result;
/* Default ascending order */
if (typeof asc == "undefined") asc = true;
if (a === null) return 1;
if (b === null) return -1;
if (a === null && b === null) return 0;
result = a - b;
if (isNaN(result)) {
return (asc) ? a.toString().localeCompare(b) : b.toString().localeCompare(a);
}
else {
return (asc) ? result : -result;
}
}
function sortByDataString(a, b) {
if (a === null) {
return 1;
}
if (b === null) {
return -1;
}
if (isNumber(a) && isNumber(b)) {
if (parseInt(a,10) === parseInt(b,10)) {
return 0;
}
return parseInt(a,10) > parseInt(b,10) ? 1 : -1;
}
if (isNumber(a)) {
return -1;
}
if (isNumber(b)) {
return 1;
}
if (a === b) {
return 0;
}
return a > b ? 1 : -1;
}
fiddle here: http://jsfiddle.net/gxFGN/6/
I left out the order parameter, but you could always reverse the array at the end if needed.
Use this:
function typeOrder(x) {
if (x == null)
return 2;
if (isNaN(+x))
return 1;
return 0;
}
function sortNumber(a, b) {
a = parseInt(a, 10); b = parseInt(b, 10);
if (isNaN(a) || isNaN(b))
return 0;
return a - b;
}
function sortString(a, b) {
if (typeof a != "string" || typeof b != "string")
return 0;
return +(a > b) || -(b > a);
}
order = order == "dsc" ? -1 : 1;
numericArr.sort(function(a, b) {
return order * ( typeOrder(a)-typeOrder(b)
|| sortNumber(a, b)
|| sortString(a, b)
);
});
(updated fiddle)
I'm pretty sure that your problem is a red herring... the abstract function that you past into sort doesn't get a third parameter (in your case _order). So in your situation that's always going to be undefined.
Please reconsider your code with that in mind and see what you get.
The array you specify is entirely Numeric so your sort should work correctly, though as other commenters have suggested, if your array ever winds up with string values (i.e. "10", "-7" etc) you'll want to parseInt and test for isNaN before doing your comparison.

sort objects by a property values

I have some objects in an array and want to have them sorted. The objects should be sorted by a metric value. So I have the following function:
objectz.sort(function(a,b){
return b.metric - a.metric;
}
The problem is that some objects have the same property values and the results of the sorting are always different.I want to additionally sort the objects with the same metric value by their name property so I get the same order of objects every time I sort them.
Thx in advance!
objectz.sort(function(a,b){
var result = b.metric - a.metric;
if (!result) return a.name > b.name ? 1 : -1;
return result;
});
Similar to zerkms:
objectz.sort(function(a,b) {
var x = b.metric - a.metric;
return x || b.name - a.name;
});
Seems to be a reverse sort (higher values occur first), is that what you want?
Edit
Note that the - operator is only suitable if the value of 'name' can be converted to a number. Otherwise, use < or >. The sort function should deal with a.name == b.name, which the > opertator on its own won't do, so you need something like:
objectz.sort(function(a,b) {
var x = b.metric - a.metric;
// If b.metric == a.metric
if (!x) {
if (b.name == a.name) {
x = 0;
else if (b.name < a.name) {
x = 1;
else {
x = -1;
}
}
return x;
});
which can be made more concise:
objectz.sort(function(a,b) {
var x = b.metric - a.metric;
if (!x) {
x = (b.name == a.name)? 0 : (b.name < a.name)? 1 : -1;
}
return x;
});
Given that the metric comparison seems to be largest to smallest order, then the ternary exrpession should be:
x = (b.name == a.name)? 0 : (b.name < a.name)? -1 : 1;
if it is required that say Zelda comes before Ann. Also, the value of name should be reduced to all lower case (or all upper case), otherwise 'zelda' and 'Ann' will be sorted in the opposite order to 'Zelda' and 'ann'

Categories

Resources