I wanted to know if this is a pure javascript function.
if it is not I would like to understand the reason.
function reverseArrayInPlace(array1) {
let array2 = [];
for (let i = array1.length - 1; i >= 0; i--) {
array2.push(array1[i]);
}
return array2;
}
Thanks for the help!
Since a pure function must produce the same output given the same input, without side effects, your code is pure (assuming that the array1 parameter will be a plain array, and not something strange like an object with a setter on its length property).
That said, purity is generally an issue to be concerned about in functional contexts, and this is not a functional approach since you're using .push, which mutates the array that you created. A more functional approach would be to use .map to create the array to be returned all in one go, without mutation:
const reverseArray = (arr) => {
const { length } = arr;
return arr.map((_, i) => arr[length - 1]);
};
or Array.from:
const reverseArray = (arr) => {
const { length } = arr;
return Array.from(
arr,
(_, i) => arr[length - 1]
);
};
Also note that all of these implementations return a new array, but they don't reverse the array in place. (Any function which reversed the array in-place would be impure, because that would be a side-effect.) Best to either choose a different name than reverseArrayInPlace, or, if you do want to reverse the array in-place, give up on purity, since it'd be impossible.
Related
I'm doing array manipulation in Javascript, and I want to be able to chain operations with multiple calls to map, concat, etc.
const someAmazingArrayOperation = (list) =>
list
.map(transformStuff)
.sort(myAwesomeSortAlgorithm)
.concat([someSuffixElement])
.precat([newFirstElement])
.filter(unique)
But the problem I've run into is that Array.precat doesn't exist. (Think of Array.concat, but the reverse.)
I don't want to modify Array.prototype in my own code, for reasons. (https://flaviocopes.com/javascript-why-not-modify-object-prototype/)
I could totally use Array.concat and concatenate my array to the end of the prefix array and carry on. But that doesn't chain with the other stuff, and it makes my code look clunky.
It's kind of a minor issue because I can easily write code to get the output I want. But it's kind of a big deal because I want my code to look clean and this seems like a missing piece of the Array prototype.
Is there a way to get what I want without modifying the prototype of a built-in type?
For more about the hypothetical Array.precat, see also:
concat, but prepend instead of append
You could use Array#reduce with a function which takes the initialValue as array for prepending data.
const
precat = (a, b) => [...a, b],
result = [1, 2, 3]
.reduce(precat, [9, 8, 7]);
console.log(result)
If you don't want to modify Array.prototype, you can consider extends:
class AmazingArray extends Array {
precat(...args) {
return new AmazingArray().concat(...args, this);
}
}
const transformStuff = x => 2*x;
const myAwesomeSortAlgorithm = (a, b) => a - b;
const someSuffixElement = 19;
const newFirstElement = -1;
const unique = (x, i, arr) => arr.indexOf(x) === i;
const someAmazingArrayOperation = (list) =>
new AmazingArray()
.concat(list)
.map(transformStuff)
.sort(myAwesomeSortAlgorithm)
.concat([someSuffixElement])
.precat([newFirstElement])
.filter(unique);
console.log(someAmazingArrayOperation([9, 2, 2, 3]));
I don't want to modify Array.prototype in my own code, for reasons.
These reasons are good, but you can sidestep them by using a collision-safe property - key it with a symbol, not a name:
const precat = Symbol('precatenate')
Array.prototype[precat] = function(...args) {
return [].concat(...args, this);
};
const someAmazingArrayOperation = (list) =>
list
.map(transformStuff)
.sort(myAwesomeCompareFunction)
.concat([someSuffixElement])
[precat]([newFirstElement])
.filter(unique);
const validateCred = arr => {
let checkableArr = arr.pop();
for (let i = arr.length - 1; i >= 0; i--) {
arr[i]
checkableArr.push(arr[i])
}
}
When i run the code, I get an error saying that .push() is not a function that I can use on checkableArr. this is because checkableArr isn't an array due to it being a variation of arr (the argument that will be passed when the function is called), which the function isn't sure is an array, is there any way to check that the argument passed into the function is an array?
EDIT:
The thing I was looking for is called isArray(), a method that returns a boolean indicating if the item passed into it is an array or no. Thanks to #David for showing me this tool, along with a bunch of helpful information that helped a lot with writing my program
You're getting that error, because you haven't made sure that the last item of the passed array (arr) is an array itself, but your function's logic requires it to be an array.
There are various ways to solve this, some of them have already been outlined by others (#hungerstar).
Check the last element of arr
One attempt is to ensure that the last element/item inside arr is an array and bail out if it isn't.
const validateCred = arr => {
let lastItem = arr.pop();
if (!Array.isArray(lastItem)) {
throw new Error('validateCred :: Last item of passed array must be an array itself');
}
// ... rest of your code ...
}
Although that does not solve the root cause, it ensures you get a decent and descriptive message about what went wrong. It's possible to improve that by defining a fallback array in case the last item isn't an array itself. Something like this:
const validateCred = arr => {
let lastItem = arr.pop();
let checkableArr = Array.isArray(lastItem) ? lastItem : [];
// ... rest of your code ...
}
One thing to note: If the last item may be an array with a value inside, you have to copy that value into the new array!
const validateCred = arr => {
let lastItem = arr.pop();
let checkableArr = Array.isArray(lastItem) ? lastItem : [lastItem]; // <--
// ... rest of your code ...
}
HINT: The following answer is based on guessing. The name validateCred lets me assume you use it to validate credentials. However, that's just guessing because all the provided code does is taking the last item and then pushing the rest of the contents of arr reversely into it (= reversing and flattening)
Reversing and flattening
If all you want to do with validateCred is reversing and flattening (and you only target supporting environments), you can easily do that with a one-liner:
// impure version
const validateCred = arr => arr.reverse().flat();
// pure version
const validateCred = arr => arr.flat().reverse();
To support older environments as well, you can use .reduce and .concat instead of .flat:
// impure version
const validateCred = arr => arr.reverse().reduce((acc, x) => acc.concat(x), []);
// pure version
const validateCred = arr => arr.reduce((acc, x) => acc.concat(x), []).reverse();
Yes, we pass an array as an argument using call/apply method. In your code when you are using arr.pop() it gets converted to number/string depending upon what type of array you specified, I specified below integer value so checkableArr is now integer so because of this you are getting an error.
Corrected code is here. Just replace in your code like:
let checkableArr = arr; //arr.pop();
I try to compare two strings in array on equal symbols or char,this code works, but how to implement it in ES6 with reduce method, if I have more than two strings an array. I need to return true if the string in the first element of the array contains all of the letters of the string in the second element of the array. But how to create the more flexible function if I have more than 2 elments in the array.
function mutation(arr) {
var arr2 = arr.map(item => item.toLowerCase().split(''));
for (i=0;i<arr2[1].length;i++) {
if (arr2[0].indexOf(arr2[1][i]) < 0)
return false;
}
return true;
}
mutation(["hello", "hey"]);
#Palaniichuk I thought your original algorithm was pretty solid. To handle your request I was able to create a solution that uses reduce.
I do have one question for you. Should the array increase in size, how would the strings be evaluated?
The reason I ask is because using a helper function like this might help you scale this algorithm. Of course it all depends on how the input changes. How the inputs are evaluated.
function makeStr(string) {
const reducer = string.split('').reduce((a, b) => {
a[b] = a[b] + 1 || 1;
return a;
}, {});
return Object.keys(reducer).sort().join('');
}
function secondMutation(arr) {
const array = [...arr].map(makeStr);
return array[0].includes(array[1]);
};
console.log(secondMutation(["hello", "hell"]));
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;
}
This question already has answers here:
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 1 year ago.
In my CouchDB reduce function I need to reduce a list of items to the unique ones.
Note: In that case it's ok to have a list, it will be a small number of items of string type.
My current way is to set keys of a object, then return the keys of that object
since the place the code can't use things like _.uniq for example.
I'd like to find a more elegant way to spell it than this.
function(keys, values, rereduce) {
// values is a Array of Arrays
values = Array.concat.apply(null, values);
var uniq = {};
values.forEach(function(item) { uniq[item] = true; });
return Object.keys(uniq);
}
The best method seem to be using ES6 and Set. Single line and faster* than above according to fiddle
const myList = [1,4,5,1,2,4,5,6,7];
const unique = [...new Set(myList)];
console.log(unique);
*tested in safari
2021 answer:
const unique = (arr) => [...new Set(arr)];
unique([1, 2, 2, 3, 4, 4, 5, 1]); // [1, 2, 3, 4, 5]
Here you just create a set from the given array and then convert it back to the array.
I measured performance and it's almost twice faster now than the approach proposed in the old answer I posted before. Also, it's just a one-liner.
Updated fiddle
Old answer just for the record:
Commonly, the approach you used is a good idea.
But I could propose a solution that will make the algorithm a lot faster.
function unique(arr) {
var u = {}, a = [];
for(var i = 0, l = arr.length; i < l; ++i){
if(!u.hasOwnProperty(arr[i])) {
a.push(arr[i]);
u[arr[i]] = 1;
}
}
return a;
}
As you can see we have only one loop here.
I've made an example that is testing both your and my solutions. Try to play with it.
An alternative that's suitable for small lists would be to ape the Unix command line approach of sort | uniq:
function unique(a) {
return a.sort().filter(function(value, index, array) {
return (index === 0) || (value !== array[index-1]);
});
}
This function sorts the argument, and then filters the result to omit any items that are equal to their predecessor.
The keys-based approach is fine, and will have better performance characteristics for large numbers of items (O(n) for inserting n items into a hashtable, compared to O(n log n) for sorting the array). However, this is unlikely to be noticeable on small lists. Moreover, with this version you could modify it to use a different sorting or equality function if necessary; with hash keys you're stuck with JavaScripts notion of key equality.
This should work with anything, not just strings:
export const getUniqueList = (a: Array<any>) : Array<any> => {
const set = new Set<any>();
for(let v of a){
set.add(v);
}
return Array.from(set);
};
the above can just be reduced to:
export const getUniqueValues = (a: Array<any>) => {
return Array.from(new Set(a));
};
:)
To get unique objects, you can use JSON.stringify and JSON.parse:
const arr = [{test: "a"}, {test: "a"}];
const unique = Array.from(new Set(arr.map(JSON.stringify))).map(JSON.parse);
console.log(unique);
Using Object.keys will give you strings if you put in integer arguments (uniq([1,2,3]) => ['1','2','3']. Here's one with Array.reduce:
function uniq(list) {
return list.reduce((acc, d) => acc.includes(d) ? acc : acc.concat(d), []);
}
This is an old question, I know. However, it is at the top of some google searches, so I wanted to add that you can combine the answers from #RobHague and #EugeneNaydenov using the following:
function unique(arr) {
const u = {};
return arr.filter((v) => {
return u[v] = !u.hasOwnProperty(v);
});
};
You can also ignore undefined values (often handy) by adding:
function unique(arr) {
const u = {};
return arr.filter((v) => {
return u[v] = (v !== undefined && !u.hasOwnProperty(v));
});
};
You can play with this solution here: https://jsfiddle.net/s8d14v5n/
I find the other answers to be rather complicated for no gain that I can see.
We can use the indexOf method of the Array to verify if an item exists in it before pushing:
const duplicated_values = ['one', 'one', 'one', 'one', 'two', 'three', 'three', 'four'];
const unique_list = [];
duplicated_values.forEach(value => {
if (unique_list.indexOf(value) === -1) {
unique_list.push(value);
}
});
console.log(unique_list);
That will work with any type of variable as well, even objects (given the identifier actually reference the same entity, merely equivalent objects are not seen as the same).
what about
function unique(list) {
for (i = 0; i<list.length; i++) {
for (j=i+1; j<list.length; j++) {
if (list[i] == list[j]) {
list.splice(j, 1);
}
}
}
}