Related
I am new to coding and trying to work on a code migration from JS to Python3. Please correct me wherever I am going wrong in conveying my question.
Here is my question
I have a js code where the function is written which finds the minimum of a particular value in the JSON file. Below is the code
var jsonarray = data["data"]["result"]
function findMinimum(data, key) {
// `filter` out the objects where the value of `cloud` matches your query
const result = data.data.result.filter(obj => {
return obj.metric.cloud?.toLowerCase() === key.toLowerCase();
// console.log(result)
// For each object return the second element of the value array
}).map(obj => {
return obj.value[1];
});
// `spread` out the array and use `Math.min` to find the minimum value
return Math.min(...result);
}
const clouds = data.data.result.map(obj => {
return obj.metric.cloud;
// console.log("test" + obj.metric.cloud)
});
// console.log(...new Set(clouds));
arry = []
arry.push(...new Set(clouds))
A detailed explanation of the JSON file and what this code is doing can be found here in my question https://stackoverflow.com/questions/72514765/how-to-find-particular-value-in-json-and-display-the-minimum-of-those-values-in]
I am trying to write this code in python3 but getting stuck while writing the filter function and spread function. Not able to set up correct syntax and merge as this function in JS. Below is the python code I tried which has many syntax errors and not working.
def findMinimum(data, key) {
result = data.data.result.filter(obj -> {
return obj.metric.cloud?.toLowerCase() === key.toLowerCase();
}).map(obj -> {
return obj.value[1];
});
return Math.min(...result);
}
clouds = data.data.result.map(obj -> {
return obj.metric.cloud;
});
arry = []
arry.append(...new Set(clouds))
Can anyone help me with this code converting or any source where I can read and understand easily about these functions in python which might be beginner-friendly?
Okay, lemme just state that python syntax is different from JS. That said the functionality is quite the same barring the data structures used in them.
Converting, mapping and filtering:
Steps to do the above task, as best i can comprehend.
Convert JSON to a python iterable or sequence. some info here
json_data_dict = json.loads(data.data.result)
Filter the data. filter(filter_func, seq) is an inbuilt function that lets filter a sequence.
filtered_iter = filter(func_name, json_data_dict)
Map the data. writes similar to filter map(map_func, seq)
mapped_iter = map(func_name, iterable)
if you are looking for chaining, functools may have some help for you. Otherwise you can compose them
filtered_then_mapped = map(map_func, filter(func_name, iterable));
more info here
Spread operator:
There is no special spread operator in python, you can just destructure lists and tuples in python like
x, y = (10, 20);
# or
my_tupl = (10, 20);
x, y = my_tuple;
the rest operator may be used like such
start, num_2, *end_nums = [0, 1, 2, 3, 4, 5]
print(start) # 0
print(num_2) # 1
print(end_nums) # [2, 3, 4, 5]
objects will be destructured using itemgetter and attrgetter from operator module
Here is my JavaScript code so far:
var linkElement = document.getElementById("BackButton");
var loc_array = document.location.href.split('/');
var newT = document.createTextNode(unescape(capWords(loc_array[loc_array.length-2])));
linkElement.appendChild(newT);
Currently it takes the second to last item in the array from the URL. However, I want to do a check for the last item in the array to be "index.html" and if so, grab the third to last item instead.
if (loc_array[loc_array.length - 1] === 'index.html') {
// do something
} else {
// something else
}
In the event that your server serves the same file for "index.html" and "inDEX.htML" you can also use: .toLowerCase().
Though, you might want to consider doing this server-side if possible: it will be cleaner and work for people without JS.
EDIT - ES-2022
Using ES-2022 Array.at(), the above may be written like this:
if (loc_array.at(-1) === 'index.html') {
// do something
} else {
// something else
}
Not sure if there's a drawback, but this seems quite concise:
arr.slice(-1)[0]
or
arr.slice(-1).pop()
Both will return undefined if the array is empty.
Use Array.pop:
var lastItem = anArray.pop();
Important : This returns the last element and removes it from the array
A shorter version of what #chaiguy posted:
Array.prototype.last = function() {
return this[this.length - 1];
}
Reading the -1 index returns undefined already.
EDIT:
These days the preference seems to be using modules and to avoid touching the prototype or using a global namespace.
export function last(array) {
return array[array.length - 1];
}
Two options are:
var last = arr[arr.length - 1]
or
var last = arr.slice(-1)[0]
The former is faster, but the latter looks nicer
http://jsperf.com/slice-vs-length-1-arr
Performance
Today 2020.05.16 I perform tests of chosen solutions on Chrome v81.0, Safari v13.1 and Firefox v76.0 on MacOs High Sierra v10.13.6
Conclusions
arr[arr.length-1] (D) is recommended as fastest cross-browser solution
mutable solution arr.pop() (A) and immutable _.last(arr) (L) are fast
solutions I, J are slow for long strings
solutions H, K (jQuery) are slowest on all browsers
Details
I test two cases for solutions:
mutable: A,
B,
C,
immutable: D,
E,
F,
G,
H,
I,
J (my),
immutable from external libraries: K,
L,
M,
for two cases
short string - 10 characters - you can run test HERE
long string - 1M characters - you can run test HERE
function A(arr) {
return arr.pop();
}
function B(arr) {
return arr.splice(-1,1);
}
function C(arr) {
return arr.reverse()[0]
}
function D(arr) {
return arr[arr.length - 1];
}
function E(arr) {
return arr.slice(-1)[0] ;
}
function F(arr) {
let [last] = arr.slice(-1);
return last;
}
function G(arr) {
return arr.slice(-1).pop();
}
function H(arr) {
return [...arr].pop();
}
function I(arr) {
return arr.reduceRight(a => a);
}
function J(arr) {
return arr.find((e,i,a)=> a.length==i+1);
}
function K(arr) {
return $(arr).get(-1);
}
function L(arr) {
return _.last(arr);
}
function M(arr) {
return _.nth(arr, -1);
}
// ----------
// TEST
// ----------
let loc_array=["domain","a","b","c","d","e","f","g","h","file"];
log = (f)=> console.log(`${f.name}: ${f([...loc_array])}`);
[A,B,C,D,E,F,G,H,I,J,K,L,M].forEach(f=> log(f));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js" integrity="sha256-VeNaFBVDhoX3H+gJ37DpT/nTuZTdjYro9yBruHjVmoQ=" crossorigin="anonymous"></script>
Example results for Chrome for short string
Here's how to get it with no effect on the original ARRAY
a = [1,2,5,6,1,874,98,"abc"];
a.length; //returns 8 elements
If you use pop(), it will modify your array
a.pop(); // will return "abc" AND REMOVES IT from the array
a.length; // returns 7
But you can use this so it has no effect on the original array:
a.slice(-1).pop(); // will return "abc" won't do modify the array
// because slice creates a new array object
a.length; // returns 8; no modification and you've got you last element
Getting the last item is possible via the length property. Since the array count starts at 0, you can pick the last item by referencing the array.length - 1 item
const arr = [1,2,3,4];
const last = arr[arr.length - 1];
console.log(last); // 4
Another option is using the new Array.prototype.at() method which takes an integer value and returns the item at that index. Negative integers count back from the last item in the array so if we want the last item we can just pass in -1
const arr = [1,2,3,4];
const last = arr.at(-1);
console.log(last); // 4
Another option is using the new findLast method. You can see the proposal here
const arr = [1,2,3,4];
const last = arr.findLast(x => true);
console.log(last); // 4
Another option is using the Array.prototype.slice() method which returns a shallow copy of a portion of an array into a new array object.
const arr = [1,2,3,4];
const last = arr.slice(-1)[0];
console.log(last); // 4
The "cleanest" ES6 way (IMO) would be:
const foo = [1,2,3,4];
const bar = [...foo].pop();
This avoids mutating foo, as .pop() would had, if we didn't used the spread operator.
That said, I like aswell the foo.slice(-1)[0] solution.
const [lastItem] = array.slice(-1);
Array.prototype.slice with -1 can be used to create a new Array containing only the last item of the original Array, you can then use Destructuring Assignment to create a variable using the first item of that new Array.
const lotteryNumbers = [12, 16, 4, 33, 41, 22];
const [lastNumber] = lotteryNumbers.slice(-1);
console.log(lotteryNumbers.slice(-1));
// => [22]
console.log(lastNumber);
// => 22
const [y] = x.slice(-1)
Quick Explanation:
This syntax [y] = <array/object> is called destructuring assignment & according to Mozilla docs, the destructuring assingment makes possible to unpack values from an array or properties from an object into distinct variables
Read more about it: here
I'd rather use array.pop() than indexes.
while(loc_array.pop()!= "index.html"){
}
var newT = document.createTextNode(unescape(capWords(loc_array[loc_array.length])));
this way you always get the element previous to index.html (providing your array has isolated index.html as one item). Note: You'll lose the last elements from the array, though.
You can use relative indexing with Array#at:
const myArray = [1, 2, 3]
console.log(myArray.at(-1))
// => 3
const lastElement = myArray[myArray.length - 1];
This is the best options from performance point of view (~1000 times faster than arr.slice(-1)).
You can use this pattern...
let [last] = arr.slice(-1);
While it reads rather nicely, keep in mind it creates a new array so it's less efficient than other solutions but it'll almost never be the performance bottleneck of your application.
If one wants to get the last element in one go, he/she may use Array#splice():
lastElement = document.location.href.split('/').splice(-1,1);
Here, there is no need to store the split elements in an array, and then get to the last element. If getting last element is the only objective, this should be used.
Note: This changes the original array by removing its last element. Think of splice(-1,1) as a pop() function that pops the last element.
Multiple ways to find last value of an array in javascript
Without affecting original array
var arr = [1,2,3,4,5];
console.log(arr.slice(-1)[0])
console.log(arr[arr.length-1])
const [last] = [...arr].reverse();
console.log(last)
let copyArr = [...arr];
console.log(copyArr.reverse()[0]);
Modifies original array
var arr = [1,2,3,4,5];
console.log(arr.pop())
arr.push(5)
console.log(...arr.splice(-1));
By creating own helper method
let arr = [1, 2, 3, 4, 5];
Object.defineProperty(arr, 'last',
{ get: function(){
return this[this.length-1];
}
})
console.log(arr.last);
Getting the last item of an array can be achieved by using the slice method with negative values.
You can read more about it here at the bottom.
var fileName = loc_array.slice(-1)[0];
if(fileName.toLowerCase() == "index.html")
{
//your code...
}
Using pop() will change your array, which is not always a good idea.
This question has been around a long time, so I'm surprised that no one mentioned just putting the last element back on after a pop().
arr.pop() is exactly as efficient as arr[arr.length-1], and both are the same speed as arr.push().
Therefore, you can get away with:
---EDITED [check that thePop isn't undefined before pushing]---
let thePop = arr.pop()
thePop && arr.push(thePop)
---END EDIT---
Which can be reduced to this (same speed [EDIT: but unsafe!]):
arr.push(thePop = arr.pop()) //Unsafe if arr empty
This is twice as slow as arr[arr.length-1], but you don't have to stuff around with an index. That's worth gold on any day.
Of the solutions I've tried, and in multiples of the Execution Time Unit (ETU) of arr[arr.length-1]:
[Method]..............[ETUs 5 elems]...[ETU 1 million elems]
arr[arr.length - 1] ------> 1 -----> 1
let myPop = arr.pop()
arr.push(myPop) ------> 2 -----> 2
arr.slice(-1).pop() ------> 36 -----> 924
arr.slice(-1)[0] ------> 36 -----> 924
[...arr].pop() ------> 120 -----> ~21,000,000 :)
The last three options, ESPECIALLY [...arr].pop(), get VERY much worse as the size of the array increases. On a machine without the memory limitations of my machine, [...arr].pop() probably maintains something like it's 120:1 ratio. Still, no one likes a resource hog.
Just putting another option here.
loc_array.splice(-1)[0] === 'index.html'
I found the above approach more clean and short onliner. Please, free feel to try this one.
Note: It will modify the original array, if you don't want to modify it you can use slice()
loc_array.slice(-1)[0] === 'index.html'
Thanks #VinayPai for pointing this out.
Here's more Javascript art if you came here looking for it
In the spirit of another answer that used reduceRight(), but shorter:
[3, 2, 1, 5].reduceRight(a => a);
It relies on the fact that, in case you don't provide an initial value, the very last element is selected as the initial one (check the docs here). Since the callback just keeps returning the initial value, the last element will be the one being returned in the end.
Beware that this should be considered Javascript art and is by no means the way I would recommend doing it, mostly because it runs in O(n) time, but also because it hurts readability.
And now for the serious answer
The best way I see (considering you want it more concise than array[array.length - 1]) is this:
const last = a => a[a.length - 1];
Then just use the function:
last([3, 2, 1, 5])
The function is actually useful in case you're dealing with an anonymous array like [3, 2, 1, 5] used above, otherwise you'd have to instantiate it twice, which would be inefficient and ugly:
[3, 2, 1, 5][[3, 2, 1, 5].length - 1]
Ugh.
For instance, here's a situation where you have an anonymous array and you'd have to define a variable, but you can use last() instead:
last("1.2.3".split("."));
ES6 object destructuring is another way to go.
const {length, [length-1]: last}=[1,2,3,4,5]
console.log(last)
You extract length property from Array using object destructuring. You create another dynamic key using already extracted key by [length-1] and assign it to last, all in one line.
For those not afraid to overload the Array prototype (and with enumeration masking you shouldn't be):
Object.defineProperty( Array.prototype, "getLast", {
enumerable: false,
configurable: false,
writable: false,
value: function() {
return this[ this.length - 1 ];
}
} );
I generally use underscorejs, with it you can just do
if (_.last(loc_array) === 'index.html'){
etc...
}
For me that is more semantic than loc_array.slice(-1)[0]
jQuery solves this neatly:
> $([1,2,3]).get(-1)
3
> $([]).get(-1)
undefined
To prevent removing last item from origin array you could use
Array.from(myArray).pop()
Mostly supported of all browsers (ES6)
In ECMAScript proposal Stage 1 there is a suggestion to add an array property that will return the last element: proposal-array-last.
Syntax:
arr.lastItem // get last item
arr.lastItem = 'value' // set last item
arr.lastIndex // get last index
You can use polyfill.
Proposal author: Keith Cirkel(chai autor)
I think this should work fine.
var arr = [1, 2, 3];
var last_element = arr.reverse()[0];
Just reverse the array and get the first element.
Edit: As mentioned below, the original array will be reversed. To avoid that you can change the code to:
var arr = [1, 2, 3];
var last_element = arr.slice().reverse()[0];
This will create a copy of the original array.
Personally I would upvote answer by kuporific / kritzikratzi. The array[array.length-1] method gets very ugly if you're working with nested arrays.
var array = [[1,2,3], [4,5,6], [7,8,9]]
array.slice(-1)[0]
//instead of
array[array.length-1]
//Much easier to read with nested arrays
array.slice(-1)[0].slice(-1)[0]
//instead of
array[array.length-1][array[array.length-1].length-1]
Whatever you do don't just use reverse() !!!
A few answers mention reverse but don't mention the fact that reverse modifies the original array, and doesn't (as in some other language or frameworks) return a copy.
var animals = ['dog', 'cat'];
animals.reverse()[0]
"cat"
animals.reverse()[0]
"dog"
animals.reverse()[1]
"dog"
animals.reverse()[1]
"cat"
This can be the worst type of code to debug!
Here is my JavaScript code so far:
var linkElement = document.getElementById("BackButton");
var loc_array = document.location.href.split('/');
var newT = document.createTextNode(unescape(capWords(loc_array[loc_array.length-2])));
linkElement.appendChild(newT);
Currently it takes the second to last item in the array from the URL. However, I want to do a check for the last item in the array to be "index.html" and if so, grab the third to last item instead.
if (loc_array[loc_array.length - 1] === 'index.html') {
// do something
} else {
// something else
}
In the event that your server serves the same file for "index.html" and "inDEX.htML" you can also use: .toLowerCase().
Though, you might want to consider doing this server-side if possible: it will be cleaner and work for people without JS.
EDIT - ES-2022
Using ES-2022 Array.at(), the above may be written like this:
if (loc_array.at(-1) === 'index.html') {
// do something
} else {
// something else
}
Not sure if there's a drawback, but this seems quite concise:
arr.slice(-1)[0]
or
arr.slice(-1).pop()
Both will return undefined if the array is empty.
Use Array.pop:
var lastItem = anArray.pop();
Important : This returns the last element and removes it from the array
A shorter version of what #chaiguy posted:
Array.prototype.last = function() {
return this[this.length - 1];
}
Reading the -1 index returns undefined already.
EDIT:
These days the preference seems to be using modules and to avoid touching the prototype or using a global namespace.
export function last(array) {
return array[array.length - 1];
}
Two options are:
var last = arr[arr.length - 1]
or
var last = arr.slice(-1)[0]
The former is faster, but the latter looks nicer
http://jsperf.com/slice-vs-length-1-arr
Performance
Today 2020.05.16 I perform tests of chosen solutions on Chrome v81.0, Safari v13.1 and Firefox v76.0 on MacOs High Sierra v10.13.6
Conclusions
arr[arr.length-1] (D) is recommended as fastest cross-browser solution
mutable solution arr.pop() (A) and immutable _.last(arr) (L) are fast
solutions I, J are slow for long strings
solutions H, K (jQuery) are slowest on all browsers
Details
I test two cases for solutions:
mutable: A,
B,
C,
immutable: D,
E,
F,
G,
H,
I,
J (my),
immutable from external libraries: K,
L,
M,
for two cases
short string - 10 characters - you can run test HERE
long string - 1M characters - you can run test HERE
function A(arr) {
return arr.pop();
}
function B(arr) {
return arr.splice(-1,1);
}
function C(arr) {
return arr.reverse()[0]
}
function D(arr) {
return arr[arr.length - 1];
}
function E(arr) {
return arr.slice(-1)[0] ;
}
function F(arr) {
let [last] = arr.slice(-1);
return last;
}
function G(arr) {
return arr.slice(-1).pop();
}
function H(arr) {
return [...arr].pop();
}
function I(arr) {
return arr.reduceRight(a => a);
}
function J(arr) {
return arr.find((e,i,a)=> a.length==i+1);
}
function K(arr) {
return $(arr).get(-1);
}
function L(arr) {
return _.last(arr);
}
function M(arr) {
return _.nth(arr, -1);
}
// ----------
// TEST
// ----------
let loc_array=["domain","a","b","c","d","e","f","g","h","file"];
log = (f)=> console.log(`${f.name}: ${f([...loc_array])}`);
[A,B,C,D,E,F,G,H,I,J,K,L,M].forEach(f=> log(f));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js" integrity="sha256-VeNaFBVDhoX3H+gJ37DpT/nTuZTdjYro9yBruHjVmoQ=" crossorigin="anonymous"></script>
Example results for Chrome for short string
Here's how to get it with no effect on the original ARRAY
a = [1,2,5,6,1,874,98,"abc"];
a.length; //returns 8 elements
If you use pop(), it will modify your array
a.pop(); // will return "abc" AND REMOVES IT from the array
a.length; // returns 7
But you can use this so it has no effect on the original array:
a.slice(-1).pop(); // will return "abc" won't do modify the array
// because slice creates a new array object
a.length; // returns 8; no modification and you've got you last element
Getting the last item is possible via the length property. Since the array count starts at 0, you can pick the last item by referencing the array.length - 1 item
const arr = [1,2,3,4];
const last = arr[arr.length - 1];
console.log(last); // 4
Another option is using the new Array.prototype.at() method which takes an integer value and returns the item at that index. Negative integers count back from the last item in the array so if we want the last item we can just pass in -1
const arr = [1,2,3,4];
const last = arr.at(-1);
console.log(last); // 4
Another option is using the new findLast method. You can see the proposal here
const arr = [1,2,3,4];
const last = arr.findLast(x => true);
console.log(last); // 4
Another option is using the Array.prototype.slice() method which returns a shallow copy of a portion of an array into a new array object.
const arr = [1,2,3,4];
const last = arr.slice(-1)[0];
console.log(last); // 4
The "cleanest" ES6 way (IMO) would be:
const foo = [1,2,3,4];
const bar = [...foo].pop();
This avoids mutating foo, as .pop() would had, if we didn't used the spread operator.
That said, I like aswell the foo.slice(-1)[0] solution.
const [lastItem] = array.slice(-1);
Array.prototype.slice with -1 can be used to create a new Array containing only the last item of the original Array, you can then use Destructuring Assignment to create a variable using the first item of that new Array.
const lotteryNumbers = [12, 16, 4, 33, 41, 22];
const [lastNumber] = lotteryNumbers.slice(-1);
console.log(lotteryNumbers.slice(-1));
// => [22]
console.log(lastNumber);
// => 22
const [y] = x.slice(-1)
Quick Explanation:
This syntax [y] = <array/object> is called destructuring assignment & according to Mozilla docs, the destructuring assingment makes possible to unpack values from an array or properties from an object into distinct variables
Read more about it: here
I'd rather use array.pop() than indexes.
while(loc_array.pop()!= "index.html"){
}
var newT = document.createTextNode(unescape(capWords(loc_array[loc_array.length])));
this way you always get the element previous to index.html (providing your array has isolated index.html as one item). Note: You'll lose the last elements from the array, though.
You can use relative indexing with Array#at:
const myArray = [1, 2, 3]
console.log(myArray.at(-1))
// => 3
const lastElement = myArray[myArray.length - 1];
This is the best options from performance point of view (~1000 times faster than arr.slice(-1)).
You can use this pattern...
let [last] = arr.slice(-1);
While it reads rather nicely, keep in mind it creates a new array so it's less efficient than other solutions but it'll almost never be the performance bottleneck of your application.
If one wants to get the last element in one go, he/she may use Array#splice():
lastElement = document.location.href.split('/').splice(-1,1);
Here, there is no need to store the split elements in an array, and then get to the last element. If getting last element is the only objective, this should be used.
Note: This changes the original array by removing its last element. Think of splice(-1,1) as a pop() function that pops the last element.
Multiple ways to find last value of an array in javascript
Without affecting original array
var arr = [1,2,3,4,5];
console.log(arr.slice(-1)[0])
console.log(arr[arr.length-1])
const [last] = [...arr].reverse();
console.log(last)
let copyArr = [...arr];
console.log(copyArr.reverse()[0]);
Modifies original array
var arr = [1,2,3,4,5];
console.log(arr.pop())
arr.push(5)
console.log(...arr.splice(-1));
By creating own helper method
let arr = [1, 2, 3, 4, 5];
Object.defineProperty(arr, 'last',
{ get: function(){
return this[this.length-1];
}
})
console.log(arr.last);
Getting the last item of an array can be achieved by using the slice method with negative values.
You can read more about it here at the bottom.
var fileName = loc_array.slice(-1)[0];
if(fileName.toLowerCase() == "index.html")
{
//your code...
}
Using pop() will change your array, which is not always a good idea.
This question has been around a long time, so I'm surprised that no one mentioned just putting the last element back on after a pop().
arr.pop() is exactly as efficient as arr[arr.length-1], and both are the same speed as arr.push().
Therefore, you can get away with:
---EDITED [check that thePop isn't undefined before pushing]---
let thePop = arr.pop()
thePop && arr.push(thePop)
---END EDIT---
Which can be reduced to this (same speed [EDIT: but unsafe!]):
arr.push(thePop = arr.pop()) //Unsafe if arr empty
This is twice as slow as arr[arr.length-1], but you don't have to stuff around with an index. That's worth gold on any day.
Of the solutions I've tried, and in multiples of the Execution Time Unit (ETU) of arr[arr.length-1]:
[Method]..............[ETUs 5 elems]...[ETU 1 million elems]
arr[arr.length - 1] ------> 1 -----> 1
let myPop = arr.pop()
arr.push(myPop) ------> 2 -----> 2
arr.slice(-1).pop() ------> 36 -----> 924
arr.slice(-1)[0] ------> 36 -----> 924
[...arr].pop() ------> 120 -----> ~21,000,000 :)
The last three options, ESPECIALLY [...arr].pop(), get VERY much worse as the size of the array increases. On a machine without the memory limitations of my machine, [...arr].pop() probably maintains something like it's 120:1 ratio. Still, no one likes a resource hog.
Just putting another option here.
loc_array.splice(-1)[0] === 'index.html'
I found the above approach more clean and short onliner. Please, free feel to try this one.
Note: It will modify the original array, if you don't want to modify it you can use slice()
loc_array.slice(-1)[0] === 'index.html'
Thanks #VinayPai for pointing this out.
Here's more Javascript art if you came here looking for it
In the spirit of another answer that used reduceRight(), but shorter:
[3, 2, 1, 5].reduceRight(a => a);
It relies on the fact that, in case you don't provide an initial value, the very last element is selected as the initial one (check the docs here). Since the callback just keeps returning the initial value, the last element will be the one being returned in the end.
Beware that this should be considered Javascript art and is by no means the way I would recommend doing it, mostly because it runs in O(n) time, but also because it hurts readability.
And now for the serious answer
The best way I see (considering you want it more concise than array[array.length - 1]) is this:
const last = a => a[a.length - 1];
Then just use the function:
last([3, 2, 1, 5])
The function is actually useful in case you're dealing with an anonymous array like [3, 2, 1, 5] used above, otherwise you'd have to instantiate it twice, which would be inefficient and ugly:
[3, 2, 1, 5][[3, 2, 1, 5].length - 1]
Ugh.
For instance, here's a situation where you have an anonymous array and you'd have to define a variable, but you can use last() instead:
last("1.2.3".split("."));
ES6 object destructuring is another way to go.
const {length, [length-1]: last}=[1,2,3,4,5]
console.log(last)
You extract length property from Array using object destructuring. You create another dynamic key using already extracted key by [length-1] and assign it to last, all in one line.
For those not afraid to overload the Array prototype (and with enumeration masking you shouldn't be):
Object.defineProperty( Array.prototype, "getLast", {
enumerable: false,
configurable: false,
writable: false,
value: function() {
return this[ this.length - 1 ];
}
} );
I generally use underscorejs, with it you can just do
if (_.last(loc_array) === 'index.html'){
etc...
}
For me that is more semantic than loc_array.slice(-1)[0]
jQuery solves this neatly:
> $([1,2,3]).get(-1)
3
> $([]).get(-1)
undefined
To prevent removing last item from origin array you could use
Array.from(myArray).pop()
Mostly supported of all browsers (ES6)
In ECMAScript proposal Stage 1 there is a suggestion to add an array property that will return the last element: proposal-array-last.
Syntax:
arr.lastItem // get last item
arr.lastItem = 'value' // set last item
arr.lastIndex // get last index
You can use polyfill.
Proposal author: Keith Cirkel(chai autor)
I think this should work fine.
var arr = [1, 2, 3];
var last_element = arr.reverse()[0];
Just reverse the array and get the first element.
Edit: As mentioned below, the original array will be reversed. To avoid that you can change the code to:
var arr = [1, 2, 3];
var last_element = arr.slice().reverse()[0];
This will create a copy of the original array.
Personally I would upvote answer by kuporific / kritzikratzi. The array[array.length-1] method gets very ugly if you're working with nested arrays.
var array = [[1,2,3], [4,5,6], [7,8,9]]
array.slice(-1)[0]
//instead of
array[array.length-1]
//Much easier to read with nested arrays
array.slice(-1)[0].slice(-1)[0]
//instead of
array[array.length-1][array[array.length-1].length-1]
Whatever you do don't just use reverse() !!!
A few answers mention reverse but don't mention the fact that reverse modifies the original array, and doesn't (as in some other language or frameworks) return a copy.
var animals = ['dog', 'cat'];
animals.reverse()[0]
"cat"
animals.reverse()[0]
"dog"
animals.reverse()[1]
"dog"
animals.reverse()[1]
"cat"
This can be the worst type of code to debug!
I'm writing compiler from kind of functional language to JS. Compiler would run in browser. I need to implement pattern matching mechanics in JS, because original language have one. I've found Sparkler and Z. Sparkler can't be executed in browser as far as I know and Z doesn't have all possibilities I need.
So my language have semantics like this:
count x [] <- 0
count x [ x : xs ] <- 1 + count x xs
count x [ y : xs ] <- count x xs
This is what happens in this snippet:
First line is definition of a function, which takes two parameters: some variable x and empty list, and returns zero.
Second line is definition of a function, which also takes two parameters: some variable x and list, which starts with x, and returns 1 + count(x, xs)
Fot this example I want to generate code like this:
const count = (x, list) => {
match(x, list) => (
(x, []) => {...}
(x, [ x : xs ]) => {...}
(x, [ y : xs ]) => {...}
)
}
How properly unfold this kind of pattern matching into ifs and ors?
General case
There is a proposal for Pattern Matching in ECMAScript, but as of 2018 it's in a very early stage.
Currently, the Implementations section only lists:
Babel Plugin
Sweet.js macro (NOTE: this isn't based on the proposal, this proposal is partially based on it!)
List case
Use destructuring assignment, like:
const count = list => {
const [x, ...xs] = list;
if (x === undefined) {
return 0;
} else if (xs === undefined) {
return 1;
} else {
return 1 + count(xs);
}
}
Using ex-patterns, you could write your example as follows. You need to use the placeholder names that come with the package (_, A, B, C, ... Z) but you can rename matched variables in the callback function with destructuring (an object containing all named matches is passed in as the first argument to the callback function).
import { when, then, Y, _, tail, end } from 'ex-patterns';
const count = list => (
when(list)
([], then(() => 0)) // match empty array
([_], then(() => 1)) // match array with (any) 1 element
([_, tail(Y)], then(({ Y: xs }) => 1 + count(xs))) // match array and capture tail
(end);
);
This also covers the case where list = [undefined, 'foo', 'bar'], which I don't think would be covered by the accepted answer.
To make the code more efficient, you can call count with an Immutable.js List instead of an array (no changes required). In that case, the tail portion of the array doesn't need to be sliced and copied into a new array on every loop.
As with the packages you mentioned, this doesn't run in the browser natively, but I guess that's not a major obstacle with modern bundling tools.
Here are the docs: https://moritzploss.github.io/ex-patterns/
Disclaimer: I'm the author of ex-patterns :)
I had a need for pattern matching and made something that works for me.
const count = patroon(
[_], ([, ...xs]) => 1 + count(xs),
[], 0
)
count([0,1,2,3])
4
See readme for more usage examples.
https://github.com/bas080/patroon
https://www.npmjs.com/package/patroon
I was wondering what would be the best way to split an array into two different arrays using JavaScript, but to keep it in the realms of functional programming.
Let's say that the two arrays should be created depending on some logic. For instance splitting one array should only contain strings with less than four characters and the other the rest.
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
I have thought about different methods:
Filter:
const lessThanFour = arr.filter((animal) => {
return animal.length < 4;
});
const fourAndMore = arr.filter((animal) => {
return animal.length >= 4;
});
The problem with this for me is that you have to go through your data twice, but it is very readable. Would there be a massive impact doing this twice if you have a rather large array?
Reduce:
const threeFourArr = arr.reduce((animArr, animal) => {
if (animal.length < 4) {
return [[...animArr[0], animal], animArr[1]];
} else {
return [animArr[0], [...animArr[1], animal]];
}
}, [[], []]);
Where the array's 0 index contains the array of less than four and the 1 index contains the array of more than three.
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays. I've thought about building an object with the reduce, but I can't imagine that it would be better than the array within an array solution.
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
Are there any other ways of doing this? (functional of course)
collateBy
I just shared a similar answer here
I like this solution better because it abstracts away the collation but allows you to control how items are collated using a higher-order function.
Notice how we don't say anything about animal.length or < 4 or animals[0].push inside collateBy. This procedure has no knowledge of the kind of data you might be collating.
// generic collation procedure
const collateBy = f => g => xs => {
return xs.reduce((m,x) => {
let v = f(x)
return m.set(v, g(m.get(v), x))
}, new Map())
}
// custom collator
const collateByStrLen4 =
// collate by length > 4 using array concatenation for like elements
// note i'm using `[]` as the "seed" value for the empty collation
collateBy (x=> x.length > 4) ((a=[],b)=> [...a,b])
// sample data
const arr = ['horse','elephant','dog','crocodile','cat']
// get collation
let collation = collateByStrLen4 (arr)
// output specific collation keys
console.log('greater than 4', collation.get(true))
console.log('not greater than 4', collation.get(false))
// output entire collation
console.log('all entries', Array.from(collation.entries()))
Check out that other answer I posted to see other usage varieties. It's a pretty handy procedure.
bifilter
This is another solution that captures both out outputs of a filter function, instead of throwing away filtered values like Array.prototype.filter does.
This is basically what your reduce implementation does but it is abstracted into a generic, parameterized procedure. It does not use Array.prototype.push but in the body of a closure, localized mutation is generally accepted as OK.
const bifilter = (f,xs) => {
return xs.reduce(([T,F], x, i, arr)=> {
if (f(x, i, arr) === false)
return [T, [...F,x]]
else
return [[...T,x] ,F]
}, [[],[]])
}
const arr = ['horse','elephant','dog','crocodile','cat']
let [truthy,falsy] = bifilter(x=> x.length > 4, arr)
console.log('greater than 4', truthy)
console.log('not greater than 4', falsy)
Though it might be a little more straightforward, it's not nearly as powerful as collateBy. Either way, pick whichever one you like, adapt it to meet your needs if necessary, and have fun !
If this is your own app, go nuts and add it to Array.prototype
// attach to Array.prototype if this is your own app
// do NOT do this if this is part of a lib that others will inherit
Array.prototype.bifilter = function(f) {
return bifilter(f,this)
}
The function you are trying to build is usually known as partition and can be found under that name in many libraries, such as underscore.js. (As far as I know its not a builtin method)
var threeFourArr = _.partition(animals, function(x){ return x.length < 4 });
I don't like this too much, because it seems that the data structure is going to give a bit of problems, seeing that it is an array of arrays
Well, that is the only way to have a function in Javascript that returns two different values. It looks a bit better if you can use destructuring assignment (an ES6 feature):
var [smalls, bigs] = _.partition(animals, function(x){ return x.length < 4 });
Look at it as returning a pair of arrays instead of returning an array of arrays. "Array of arrays" suggests that you may have a variable number of arrays.
I've managed to look at similar questions online as well as Stack Overflow, but many of these break the idea of immutability by using push() or they have very unreadable implementations, which in my opinion breaks the expressiveness of functional programming.
Mutability is not a problem if you localize it inside a single function. From the outside its just as immutable as before and sometimes using some mutability will be more idiomatic than trying to do everything in a purely functional manner. If I had to code a partition function from scratch I would write something along these lines:
function partition(xs, pred){
var trues = [];
var falses = [];
xs.forEach(function(x){
if(pred(x)){
trues.push(x);
}else{
falses.push(x);
}
});
return [trues, falses];
}
A shorter .reduce() version would be:
const split = arr.reduce((animArr, animal) => {
animArr[animal.length < 4 ? 0 : 1].push(animal);
return animArr
}, [ [], [] ]);
Which might be combined with destructuring:
const [ lessThanFour, fourAndMore ] = arr.reduce(...)
If you are not opposed to using underscore there is a neat little function called groupBy that does exactly what you are looking for:
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
var results = _.groupBy(arr, function(cur) {
return cur.length > 4;
});
const greaterThanFour = results.true;
const lessThanFour = results.false;
console.log(greaterThanFour); // ["horse", "elephant", "crocodile"]
console.log(lessThanFour); // ["dog", "cat"]
Kudos for the beautiful response of the user Thank you, here an alternative using a recursion,
const arr = ['horse', 'elephant', 'dog', 'crocodile', 'cat'];
const splitBy = predicate => {
return x = (input, a, b) => {
if (input.length > 0) {
const value = input[0];
const [z, y] = predicate(value) ? [[...a, value], b] : [a, [...b, value]];
return x(input.slice(1), z, y);
} else {
return [a, b];
}
}
}
const splitAt4 = splitBy(x => x.length < 4);
const [lessThan4, fourAndMore ] = splitAt4(arr, [], []);
console.log(lessThan4, fourAndMore);
I don't think there could be another solution than returning an array of arrays or an object containing arrays. How else is a javascript function return multiple arrays after splitting them?
Write a function containing your push logic for readability.
var myArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var x = split(myArr, v => (v <= 5));
console.log(x);
function split(array, tester) {
const result = [
[],
[]
];
array.forEach((v, i, a) => {
if (tester(v, i, a)) result[0].push(v);
else result[1].push(v);
});
return result;
}