ramda equivalent for rotate - javascript

Given a rotate function like the one below, which rotates the array a set number of slots.
Is there an equivalent Ramda.js function or composition that will do this rotation?
var test = [1,2,3,4,5,6,7,8,9];
function rotate(arr, count) {
arr = arr.slice();
while (count < 0) {
count += arr.length;
}
count %= arr.length;
if (count) {
arr.splice.apply(arr, [0, 0].concat([].slice.call(arr.splice(arr.length - count, count))));
}
return arr;
}
example
rotate(test, 2) // -> [8, 9, 1, 2, 3, 4, 5, 6, 7]

Here's a point-free one liner which takes the count first and the data second, consistent with ramda's composable style:
const rotate = pipe(splitAt, reverse, flatten);
Of course you can always flip(rotate) to get a data first version.
UPDATE
Sorry, I read too fast and assumed the normal, left-wise direction for the rotation (eg, as it is in ruby). Here's a variation of the idea that does what your original does:
const rotate = pipe(useWith(splitAt, [negate, identity]), reverse, flatten);

This is similar to #donnut's answer, but includes modulo arithmetic to handle counts which exceed the length of the given list:
var rotate2 = function(xs, count) {
var n = -(count % xs.length);
return R.concat(R.slice(n, Infinity, xs),
R.slice(0, n, xs));
};
Here's a mutation-free equivalent which doesn't use Ramda at all:
var rotate3 = function(xs, count) {
var n = -(count % xs.length);
return xs.slice(n).concat(xs.slice(0, n));
};
Both solutions are significantly more declarative than the solution in the original post.

You could try:
function reverse(arr, count) {
return R.concat(R.slice(arr.length-count, arr.length, arr), R.slice(0, arr.length-count, arr));
}
See http://bit.ly/1G90ny8

Related

Can this selection sort implementation be made more efficient or elegant?

I have made a basic implementation of Selection sort, using Math.min() of javascript. Can anyone point out ways in which one can make this more efficient or elegant? Something that I could have avoided doing, etc? Thanks everyone, the code is below:
let arr = [2, 0, 5, 1, 3, 2, 6, 4, 9, 0, 10, 2, 14, 8];
function selectionSort(array) {
let workingArr = [...array]; //don't want to modify original array
let sortedArr = []; //this will be returned as result
for (let i = 0; i < array.length; i++) {
let sliced = workingArr.slice(0);
let min = Math.min(...sliced); //minimum of the slice
sortedArr[i] = min;
let index = workingArr.indexOf(min);
workingArr.splice(index, 1);
}
return sortedArr;
}
let x = selectionSort(arr);
console.log(x);
document.body.innerHTML = x;
I am not sure about the definition of selection sort being used here but here you have two versions of your code where: 1) you remove unnecessary copies of arrays (space inefficient) and 2) you have a more elegant solution.
Your original solution optimised
function selectionSort(array) {
const localArr = [...array];
const res = [];
for (let i = 0; i < localArr.length; i++) {
const min = Math.min(...localArr);
localArr.splice(localArr.indexOf(min), 1);
i--;
res.push(min);
}
return res;
}
Use Array.prototype.reduce
function selectionSort(array) {
const localArr = [...array];
return array.reduce((acc) => {
const min = Math.min(...localArr);
localArr.splice(localArr.indexOf(min), 1);
return acc.concat(min);
}, []);
}
Note: in your original version of the function you seemed to care about immutability. Then in the body of the function you use Array.prototype.splice and Array.prototype.push which both contravene the FP principle of immutability. I am not using a pure FP approach here just for brevity but you should look into other arrays methods that are more 'reliable' so to speak.
It seems nobody found anything here. But I finally found something that could have been avoided in original code. I figured out that there is no need to make slices of the array named workingArr in code above (in the question). Here is the modified code which is simpler.
let arr = [2, 0, 5, 1, 3, 2, 6, 4, 9, 0, 10, 2, 14, 8];
function selectionSort(array) {
let workingArr = [...array]; //don't want to modify original array
let sortedArr = []; //this will be returned as result
for (let i = 0; i < array.length; i++) {
//run upto full length of original array
let min = Math.min(...workingArr); //minimum of the slice
sortedArr[i] = min; //minimum found inserted into sortedArr
let index = workingArr.indexOf(min); //find inserted ele's position in original input array's copy, so that we can use it to removed ele from that same array (otherwise in next pass that element will still come out as min)
workingArr.splice(index, 1);
}
return sortedArr; //return resulting array
}
let x = selectionSort(arr);
console.log(x);
console.log(x.reverse()); //for descending sort

Find 2nd largest value in an array that has duplicates of the largest integer

I'm trying to find the second largest number in an array of numbers, but the greatest number appears twice, so I can't just remove it from the array and select the new highest number.
array = [0, 3, 2, 5, 5] (therefore 3 is the 2nd largest value)
I have this code where I can explicitly return 3, but it wouldn't work on other arrays:
function getSecondLargest(nums) {
var sorted_array = nums.sort(function (a,b) {return a - b;});
var unique_sorted_array = sorted_array.filter(function(elem, index, self) {
return index === self.indexOf(elem);
})
return unique_sorted_array[unique_sorted_array.length - 2];
}
return unique_sorted_array[unique_sorted_array.length - 2];
If I wanted to make it more dynamic, is there a way that I could identify the greatest value of the array, then compare that against each iteration of the array?
I was thinking that something along the lines of:
var greatestNum = sortedArray[-1]
while(greatestNum != i) do {
//check for the first number that doesn't equal greatestNum
}
Any help would be appreciated.
You can simply create a Set first and than sort in descending and take the 1st index element
let array = [0, 3, 2, 5, 5]
let op = [...new Set(array)].sort((a,b) => b-a)[1]
console.log(op)
For those who thinks in terms of efficiency. this is the best way IMO
let array = [0, 3, 2, 5, 5]
let max = -Infinity
let secondMax = -Infinity
for(let i=0; i<array.length; i++){
if(array[i] > max){
secondMax = max
max = array[i]
}
}
console.log(secondMax)
I’d recommend doing something more like
const nums = [0, 3, 2, 5, 5];
nums.sort(function (a,b) {return b - a;})
for (let i = 1; i < nums.length; i++) {
if (nums[0] !== nums[i]) {
return nums[i];
}
}
which should be a lot more efficient (especially in terms of memory) than converting to a set and back...
Try this:
var intArray = stringArray.map(nums); // now let's sort and take the second element :
var second = intArray.sort(function(a,b){return b-a})[1];
};
For those who wants to do this using Math.max(). Here's the simplest way to do this.
const getSecondLargest = function (arr) {
const largest = Math.max.apply(null, arr);
for (let i = 0; i < arr.length; i++) {
if (largest === arr[i]) {
arr[i] = -Infinity;
}
}
return Math.max.apply(null, arr);
};
console.log(getSecondLargest([3, 5, 9, 9, 9])); //5
Side note: Math.max() don't take an array, so we have to use Math.max.apply() to pass the array in the function. -Infinity is smaller than any negative finite number.

Slice into chunks and average (reduce) array in JS

I have an array:
var data = [0,1,2,3,4,5];
that I would like to splice into [0,1,2] and [3,4,5] followed by averaging it so the final result would be:
var dataOptimised = [1,4];
this is what I have found so far:
function chunk (arr, len) {
var chunks = [];
var i = 0;
var n = arr.length;
while (i < n) {
chunks.push(arr.slice(i, i += len)); // gives [[0,1,2] [3,4,5]]
}
return chunks;
};
How to reduce it?
Thanks
Sum each chunk using Array.reduce() and divide by the chunk's length.
var data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30];
function chunkAverage(arr, len) {
var chunks = [];
var i = 0;
var n = arr.length;
var chunk;
while (i < n) {
chunk = arr.slice(i, i += len);
chunks.push(
chunk.reduce((s, n) => s + n) / chunk.length
);
}
return chunks;
};
console.log(chunkAverage(data, 3));
You can map the array and reduce it.
function chunk(arr, len) {
var chunks = [];
var i = 0;
var n = arr.length;
while (i < n) {
chunks.push(arr.slice(i, i += len)); // gives [[0,1,2] [3,4,5]]
}
return chunks;
};
var data = [0, 1, 2, 3, 4, 5];
var result = chunk(data, 3).map(o => (o.reduce((c, v) => c + v, 0)) / o.length);
console.log(result);
Split the array in half using splice. And use .reduce to take sum and average finally
var arrR = [0, 1, 2, 3, 4, 5],
arrL = arrR.splice(0, Math.ceil(arrR.length / 2)),
results = [getAverave(arrL), getAverave(arrR)];
console.log(results)
function getAverave(arr) {
return arr.reduce(function(a, b) {
return a + b;
}) / arr.length;
}
Here is the sortest answer possible for this question.
n is the index you want to slice from.
function chunk (arr, n) {
return [Math.sum.apply(null, arr.slice(0, n)), Math.sum.apply(null, arr.slice(n, arr.length))];
};
If you don't mind using underscore.js, you can use the _.chunk() function to easily chunk your array, then map() each chunk to a reduce() function which averages each chunk.
When importing the underscore.js library, you can reference the library using the _ symbol.
const arr = [0, 1, 2, 3, 4, 5];
const len = 3;
const result = _.chunk(arr, len).map(chunk => chunk.reduce((a, b) => a + b, 0) / chunk.length);
console.log(result); // Outputs [1, 4]
If you have an odd-length array; say that arr = [0, 1, 2, 3, 4, 5, 6], then result would be [1, 4, 6].
In HTML, you can include the library in a <script> tag:
<script src="http://underscorejs.org/underscore.js"></script>
Here's a working jsfiddle where you can see it in action (you'll have to open the F12 tools to see console output; the StackOverflow embedded snippets are kind of funky and don't work right).
Agreeing with both Ori Drori and Eddie, but I thought I might also provide some additional minor changes to your chunk function for consistency and maintainability's sake...
When writing JavaScript, I would recommend using function names that won't collide with common/expected variable names. For example, with a function like chunk() that returns a "chunk", it's likely you would want to create a variable called chunk to hold its return value. A line of code like var chunk = chunk() is an obvious problem, but if it gets any less direct it can easily wreak havoc down the line. Using the const var = function pattern (see snippet) helps you avoid writing over the original function by throwing an error on the correct line, but I would argue it's also still good to get in the habit of using a naming convention that doesn't have this drawback just in case you can't use something like const. My approach is to always include a verb in the function name. In your case, "chunk" can also be considered a verb, but it conflicts. So, I prefixed it with "get".
const getChunk = (arr, len) => {
const chunks = []
const n = arr.length
let i = 0
while (i < n) {
chunks.push(arr.slice(i, i += len))
}
return chunks
}
const data = [0,1,2,3,4,5]
const optimizedData =
getChunk(data, 3).map(chunk =>
chunk.reduce((total, val) => total + val) / chunk.length
)
console.log(optimizedData)

replace array element with a different number, in javascript

I am trying to write a function that takes in an array and a max cutoff number. The number is the max number that the string can contain, and this number replaces all array elements greater than the number. For example, if the array is [1,2,3,4,5,6] and the cutoff number is 4, the output should be [1,2,3,4,4,4].
This is what I have so far, but I am only getting [1,2,3,4,5] as the output instead of [1,2,3,3,3]. Any suggestions??
var maxCutoff = function(array, number) {
for (var i=0; i<array.length; i++) {
if (array.indexOf(i) > number) {
array.pop();
array.push(number);
return array;
} else if (array.indexOf(i) <= number) {
return array;
}
}
}
console.log(maxCutoff([1,2,3,4,5], 3));
You can deal with it with a simple one-liner, using Array#map.
const maxCutoff = (arr, num) => arr.map(v => v > num ? num : v);
console.log(maxCutoff([1,2,3,4,5], 3));
You could use a ternary for the result for mapping.
const maxCutoff = (array, value) => array.map(a => a > value ? value : a);
console.log(maxCutoff([1, 2, 3, 4, 5], 4));
or Math.min for the result for mapping.
const maxCutoff = (array, value) => array.map(a => Math.min(a, value));
console.log(maxCutoff([1, 2, 3, 4, 5], 4));
The solution is quite simple, you just need to change the element to max.
Assuming you are a beginner to JS, the below solution should be more comprehendable to you. Another answer that uses map, might come off as magical, that should be more efficient, but it depends on how well the browser handles the map function, for most cases, browsers do implement it quite fine
var maxCutoff = function(array, number) {
for (var i=0; i<array.length; i++) {
if(array[i]>number)
array[i] = number;
}
return array
}
console.log(maxCutoff([1,2,3,4,5], 3));

Can two loops be merged into one?

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);

Categories