Related
I wanted to write a function to get all numbers, what is greater than all elements to its right.
Example if i have an array like this:
arr = [ 75,47,42,56,13,55];
I want a result like this [75,56,55]in a new array.
Other example if i have an array like this:
arr = [16,17,14,3,14,5,2]
I want a result like: [17,14,5,2]
What methods can i use to get this result whatever numbers i have in an array?
You can use filter the array. splice the array to get all the numbers on the right. Use every to check if all array elements are greater than the value.
let arr = [75, 47, 42, 56, 13, 55];
let result = arr.filter((v, i, a) => [...a].splice(i + 1, a.length).every(o => v > o));
console.log(result);
Doc: filter(), splice(), every()
You could simply iterate from the right side and check against the latest found greatest value.
function greaterThanRight(array) {
return array.reduceRight((r, v) => [].concat(v <= r[0] ? [] : v, r), [])
}
console.log([[75, 47, 42, 56, 13, 55], [16, 17, 14, 3, 14, 5, 2]].map(greaterThanRight).map(a => a.join(' ')));
A simple for loop where inner loop has index starting with the index value of the match having highest right value:
var arr = [ 75,47,42,56,13,55];
var res = [];
for(var i=0; i<arr.length; i++){
var highestValue = arr[i];
for(var j=i+1; j<arr.length; j++){
if(highestValue < arr[j]){
highestValue = arr[j];
i = j;
break;
}
}
res.push(highestValue);
}
console.log(res);
var arr = [75,47,42,56,13,55];
arr.sort(function(a, b) {
// a = current item in array
// b = next item in array
return b - a;
});
var newArr = arr.slice(0,3);
console.log(newArr);
Try this. Just start checking from the right.keep a variable max and update it whenever you found a new max
var arr = [ 75,47,42,56,13,55];
function findMaxRight(arr){
var res=[]
var max = Number.MIN_SAFE_INTEGER
for(var i=arr.length -1; i>=0;i--){
if(arr[i]> max){
res.unshift(arr[i])
max = arr[i]
}
}
return res
}
console.log(findMaxRight(arr));
One way to do it could be to loop your array, get the current item and get the rest of the array after the current item using slice.
Then sort the rest of the array descending and get the first entry which is the highest one.
If the current value is larger than the highest one, it is larger then all the values on the right:
let items = [75, 47, 42, 56, 13, 55];
let result = [];
items.forEach((item, index) => {
let head = items[index];
let tail = items.slice(index + 1).sort(function(a, b) {
return b - a;
});
if ((head > tail[0]) || (index === items.length - 1)) {
result.push(head);
}
});
console.log(result);
My solution to do this : find the max and if it's the current number store it, continue with the new array looping = looping.slice(1)
let arr = [75,47,42,56,13,55];
let looping = [...arr];
let finish = arr.reduce( (acc, x) => {
if(x === Math.max(...looping)) acc.push(x);
looping = looping.slice(1);
return acc;
}, [])
console.log(finish) // [75,56,55]
I'm using the following function to add specific numbers into an array that I later want to be assigned to a variable. For this I'm using two for loops, but I feel like there is a more succinct way to do it. I tried merging the two loops in one without getting an error, but the output is not the same.
Working Example:
function fill () {
var array = [];
for (var index = 0; index < arguments.length; index++) {
for (var number = arguments[index][0]; number <= arguments[index][1]; number++)
array.push(number);
}
return array;
};
/* Use */
var keys = fill([1, 10], [32, 34]);
/* Output */
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 32, 33, 34]
Merged Example:
function fill () {
var array = [];
for (var index = 0, number = arguments[index][0];
index < arguments.length && number <= arguments[index][1];
index++ && number++) {
array.push(number);
}
return array;
};
/* Use */
var keys = fill([1, 10], [32, 34]);
/* Output */
[1, 1]
Is it possible to actually merge the two loops into one? If not, is there a way to write the foregoing function in less code?
Your code in the first example is fine. There is no real "clean" way to remove the nested loops.
You could iterate over them with forEach, but then you'd still have nested loops, even if one of them is disguised as a function call:
function fill () {
var array = [];
Array.prototype.slice.apply(arguments) // Make an array out of arguments.
.forEach(function(arg){
for (var number = arg[0]; number <= arg[1]; number++){
array.push(number);
}
});
return array;
};
console.log(fill([1, 10], [32, 34]));
And you'd have to use Array.prototype.slice.apply to convert arguments to an actual array. (which is ugly)
So, basically, nested loops aren't necessarily "evil". Your first example is as good as it gets.
JavaScript is a functional language. For the sake of modern coding purposes a functional approach is best for the coder's benefit.
var fillArray = (...args) => args.reduce((res,arg) => res.concat(Array(...Array(arg[1]-arg[0]+1)).map((e,i) => i + arg[0])),[]),
filled = fillArray([1, 10], [32, 34]);
console.log(filled);
OK what happens here.. It's very simple. We do the job by fillArray function. fillArray function takes indefinite number of arguments. So we collect them all in an array called args by utilizing the ES6 rest operator ....
var fillArray = (...args)
Now that we have our source arrays in the args array we will apply a reduce operation to this array with an initial value of an empty array (res). What we will do is.. as per each source (arg) array we will create a new array and then we will concatenate this to the res array. Ok we receive [1,10] as source which means we need an array of length arg[1]-arg[0]+1 right. So comes
Array(...Array(arg[1]-arg[0]+1))
we could also do like Array(arg[1]-arg[0]+1).fill() same thing. We now have an array filled with "undefinite" in the needed length. Then comes map. This is really very simple as we apply to this undefinites array like
.map((e,i) => i + arg[0]))
which means each item will be the current index + offset which is the arg[0]
Then we concatenate this array to our results array and pass to the next source array. So you see it is very straight forward and maintainable.
You might not be able to escape the two loops, but that shouldn't necessarily be a goal either. The loop themselves aren't really harmful – it's only if you're iterating over the same data multiple times that you might want to reconsider your code
Consider this entirely different approach
const range = (x , y) =>
x > y
? []
: [ x, ...range (x + 1, y) ]
const concat = (xs, ys) =>
xs .concat (ys);
const flatMap = (f, xs) =>
xs .reduce ((acc, x) => concat (acc, f (x)), [])
const apply = f => xs =>
f (...xs)
const fill = (...ranges) =>
flatMap (apply (range), ranges);
console.log
(fill ( [1,10]
, [32,34]
, [40,49]
, [100,100]
)
)
So yes, #Redu is on the right track with "JavaScript is a functional language", but I think his/her answer falls short of delivering a well-composed functional answer.
The answer above shows how functions with individualized concerns can be easy to read, easy to write, and easy to combine to achieve complex computations.
In ES6, you could use the rest operator and build a new array, based on the items.
function fill(...p) {
return p.reduce((r, a) => r.concat(Array.apply(null, { length: a[1] - a[0] + 1 }).map(() => a[0]++)), []);
};
var keys = fill([1, 10], [32, 34]);
console.log(keys);
Similar to another answer, but a little more complete:
const arrays = [[1,10],[32,34],[9,12]]
const range = (a,b) => a >= b ? [] :
[...Array(b-a).keys()].map(i => i+a)
const result = arrays.reduce( (a,c) =>
a.concat( range(c[0], c[1]+1) ), [] )
// => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 32, 33, 34, 9, 10, 11, 12 ]
If you prefer a more traditional range function, then:
const arrays = [[1,10],[32,34],[9,12]]
function range(a,b) {
var arr = []
for (let i = a; i < b; i++)
arr.push(i)
return arr
}
const result = arrays.reduce( function(a,c) {
return a.concat( range(c[0], c[1]+1) )
}, [] )
After almost 2 years and some great answers that were posted to this thread proposing interesting alternatives, I found a way to merge the two loops into one, but it ain't pretty!
Code:
function fill () {
var
ar = [],
imax = arguments.length,
/* The functions that calculate the bounds of j. */
jmin = i => arguments[i][0],
jmax = i => arguments[i][1] + 1;
for (
let i = 0, j = jmin(i);
i < imax && j < jmax(i);
/* If j reaches max increment i and if i is less than max set j to min. */
ar.push(j++), (j == jmax(i)) && (i++, (i < imax) && (j = jmin(i)))
);
return ar;
};
/* Use */
var keys = fill([1, 10], [32, 34], [76, 79]);
console.log.apply(console, keys);
I have a list of numbers (any length) ranging from 0 - 100, including duplicates. I need convert those numbers to portions of 100.
For example:
[25, 50] becomes [33.333, 66.666]
[25, 50, 50] becomes [20, 40, 40]
What algorithm would work best for this?
You would need to calculate the sum of the values in your array - then you could divide each value in your array by that sum, and multiply by 100.
Try this :
console.log(33.333 - 33.333 % 25); // 50
% is MODULO operator.
Number.prototype.range = function(a) {
return this - this % a;
}
console.log((33.333).range(25)); // 25;
console.log((66.666).range(25)); // 50;
In array use map like this :
console.log([33.333, 66.666].map(function(a) {
return a.range(25);
}));
Demo
This can be done by calculating the sum of the array and then dividing each value by that. It can be done easily using Array.reduce to sum and Array.map to create a new array with the final output. Here is an example:
var arr1 = [25, 50];
var arr2 = [25, 50, 50];
function proportion(arr) {
var sum = arr.reduce(function(prev, cur){
return prev + cur;
});
var result = arr.map(function(val){
return (val/sum)*100;
});
return result;
}
console.log(proportion(arr1)); // [33.33333333333333, 66.66666666666666]
console.log(proportion(arr2)); // [20, 40, 40]
JSBin here: http://jsbin.com/texuc/1/edit
The simplest way to solve this is to sum the array, find each value as a fraction of that sum, and then multiply by 100.
This code should do the trick.
var inputArray = [25, 50, 50];
var outputArray = [];
var total = 0;
for (var i=0; i<(inputArray.length); i++) {
total += inputArray[i];
}
for (var i=0; i<(inputArray.length); i++) {
outputArray[i] = ((inputArray[i])/total) * 100;
}
I have 2 arrays in javascript.
var A = ['c++', 'java', 'c', 'c#', ...];
var B = [12, 3, 4, 25, ...];
Now from these 2 arrays i want to create another array like :
[['c++',12], ['java',3], ['c',4], ['c#', 25] ...];
Both A and B arrays are variable length in my case so how can i do this?
Underscore.js is good at that:
_.zip(*arrays)
Merges together the values of each of the arrays with the values at
the corresponding position. Useful when you have separate data sources
that are coordinated through matching array indexes. If you're working
with a matrix of nested arrays, zip.apply can transpose the matrix in
a similar fashion.
_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
You can use this snippet if you don't to use any third party library:
var i = 0
, n = A.length
, C = [];
for (; i < n; i++) {
C.push([A[i], B[i]]);
}
function Merge(A,B){
var length = Math.min(A.length,B.length);
var result = [];
for(var i=0;i<length;i++){
result.push([ A[i], B[i] ])
}
return result;
}
I think that using a hashMap instead of 2 arrays could be a good solution for you.
In example, you could do something like the following:
var h = new Object(); // or just {}
h['c++'] = 12;
h['java'] = 3;
h['c'] = 4;
Take a look at:
http://www.mojavelinux.com/articles/javascript_hashes.html
I liked this question - Legitimate uses of the Function constructor - so I wanted to create a similar question regarding the Array constructor.
Of course, the array literal notation is the correct way to create arrays. This would mean that the new Array notation should not be used. And "case closed".
However, there is one specificity of the new Array form. If a natural number is passed in, an empty array is created and its length property is set to that number.
So
arr = new Array( 7 );
is equivalent to
arr = [];
arr.length = 7;
This can be considered a feature. I was wondering if this "feature" has real-world uses. I recently stumbled upon one such (simple) use:
new Array( n + 1 ).join( '*' ) // returns string containing n stars
// e.g.
new Array( 3 ).join( '*' ) // returns '**'
new Array( 6 ).join( '*' ) // returns '*****'
This is cool, but was hoping for some more advanced uses. (Something that would make the new Array notation a legitimate tool in JavaScript programs.)
Update: I've noticed that the jQuery library uses the new Array( len ) notation in one instance - it's inside the when function (search for "when:"):
when: function( firstParam ) {
var args = sliceDeferred.call( arguments, 0 ),
i = 0,
length = args.length,
pValues = new Array( length ),
count = length,
pCount = length,
// etc.
They use it to initialize the pValues local variable, which is used in a local function further down in the code:
function progressFunc( i ) {
return function( value ) {
pValues[ i ] = arguments.length > 1 ?
sliceDeferred.call( arguments, 0 ) : value;
deferred.notifyWith( promise, pValues );
};
}
I would love to know if changing the assignment to just
pValues = [],
would break the program... (Is new Array( length ) required for notifyWith to work properly?)
I don't know if this counts, but one case where you'd use the constructor is if you need to be sure you have clean, unmodified Array constructor. You can create an "iframe sandbox"
var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
var Safe = frames[frames.length - 1];
And then create arrays like this...
var things = new Safe.Array('x', 'y', 'z');
...knowing that your arrays will not have any foreign stuff in the prototype put there by other scripts.
That's not making use of that single-parameter-as-array-length feature, though. The only thing that's probably really good for is setting up huge arrays to benchmark stuff.
Pretty much anything you can come up with a homebrewn map function (native .map doesn't iterate and jQuery.map is just full bugs)
Creating ranges:
//1-20
map( Array( 20 ), function( v,i ){
return i+1;
});
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
//-50 - 0
map( Array( 51 ), function( v,i ){
return i-50;
});
//[-50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
In general, instead of doing:
var a = [],
l = 10;
while ( l-- ) {
a.unshift(
(function(l){
return function() {
alert( l );
};
})( l )
);
}
You can do this:
var a = map( Array( 10 ), function ( v, i ) {
return function(){
alert( i );
};
});
Note that this only applies to jQuery.map or shimmed .map, native .map doesn't iterate the array.
If you're not using jQuery( which flattens the result :( ) you can create
three dimensional arrays like this:
var xyz = map(Array(10), function (v, i) {
return map(Array(10), function (v, j) {
return map(Array(10), function (v, k) {
return i * 100 + j * 10 + k;
});
});
});
xyz[0][0][0] // 0
xyz[9][9][9] // 999
xyz[4][4][4] // 444
xyz[3][5][8] // 358
Of which the equivalent of for loops is pretty horrific :P
Quick implementation of a map function for completeness:
function map( elems, callback ){
var i, length = elems.length, ret = [];
for ( i = 0; i < length; i++ ) {
value = callback( elems[ i ], i );
ret[ ret.length ] = value;
}
return ret;
}
How about as an alternate way to shallow clone an Array?
var arr = [1,2,3];
var arr2 = Array.apply( null, arr );
arr === arr2; // false;
arr.length === arr2.length; // true
Yes, I'm reaching here because you'd just use .slice(), but then I really don't see using Array as illegitimate in the first place, as long as you know what you're doing.
Getting off topic, but one way to make a common function that utilizes Array or slice. Requires bind. Too lazy right now to update for older browsers.
http://jsfiddle.net/fdgBU/1/
var slicer,
apply = Function.prototype.apply;
if( apply.bind ) {
try {
(slicer = apply.bind( Array, null ))({length:0});
} catch( e ) {
slicer = apply.bind([].slice);
}
} else {
slicer = function( coll ) {
if( !coll || coll.length !== +coll.length ) return;
var res = [], i = 0, len = coll.length >>> 0;
for( ; i < len; ++i ) {
res[i] = coll[i];
}
return res;
};
}
var arr_like = {'0':true,'1':true,'2':true,length:3},
arr = slicer( arr_like );
console.log( arr ); // [true, true, true]
console.log( arr.length === arr_like.length); // true