Related
I have an array var plans=[a, b, c, d ]; with prices based monthly any yearly.
Consider- a and b are monthly and c and d are yearly.
So, I want to split the array based on the monthly and yearly values and store the values in to separate arrays
var monthly_plans=[]; and var yearly_plans=[]
So, how do I do this?
I have used the js split() function before but on a very basic level.
You can use the slice(start, end) function on arrays, e.g.
monthly_plans = plans.slice(0,2);
yearly_plans = plans.slice(2,4);
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
split() is a method of the String object, not of the Array object.
From what I understand from your question, you need the Array.prototype.slice() method instead:
The slice() method returns a shallow copy of a portion of an array
into a new array object.
Syntax
arr.slice([begin[, end]])
In conclusion, you may want to do something like this:
var monthly_plans = plans.slice(0, 2);
var yearly_plans = plans.slice(2);
And the ES5 approach:
var plans=[a, b, c, d];
var monthly_plans = plans.filter( plan => plan==='monthly condition' );
var yearly_plans = plans.filter( plan => plan==='yearly condition' );
I think it will be a better avenue to use a for.
Example:
for (var i=0;i<plans.length;i++)
{
if(plans[i] == 'monthly condition')
{
monthly_plans.push(plans[i]);
}
else
{
yearly_plans.push(plans[i]);
}
}
Based on your post, the solution will not involve split(). If you know in advance which plan designations are monthly and which are yearly:
var plans = ['a', 'b', 'c', 'd', 'm', 'y', ....... 'n'],
count = plans.length, i = 0;
var monthly_designations = ['a', 'b', 'm'],
yearly_designations = ['c', 'd', 'y'];
for(; i < count; i++) {
if (monthly_designations.indexOf(plans[i]) !== -1) {
monthly_plans.push(plans[i]);
} else {
if (yearly_designations.indexOf(plans[i]) !== -1) {
yearly_plans.push(plans[i]);
}
}
}
Then just check the plans array against the known designations to filter the contents into the correct sub-arrays monthly_plans and yearly_plans.
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;
}
I have a couple of arrays that looks a bit like these:
arr['a'] = 'val1';
arr['b'] = 'val2';
arr['c'] = 'val3';
The index is not an integer, it is a string. I want to remove arr['b'] from the array completely. I have tried:
arr.splice('b', 1);
It does not work, and it might be because the index in not an integer, according to
w3schools this is the problem "index - Required. An integer".
A possible solution could be looping through all arrays and re-creating them with an integer index and then an array holding the custom indexes as values and the equivalent integer index as its index.
This seems like a tad unnecessary and a waste of resources, is there a smarter more effective and simpler solution?
Preferably an arr.splice that will work with a non-integer index.
I have looked through plenty of posts that covers how to remove elements from arrays by index and values, but none covers how to remove elements using a non-integer index.
Example posts that I have found:
0
1
2
Any and all help is greatly appreciated!
//Edit, used following as a solution.
function aObj() {
this.a = "";
this.b = [];
}
var aObjs = [];
aObjs.push(new aObj);
aObjs.push(new aObj);
aObjs.push(new aObj);
aObjs[0].a = "val1";
aObjs.splice(1, 1);
Looks a bit different than what I used in my first example, but this is more accurate towards how I used it. May not be the best way to do it, but it works.
Don't use array for string indexes, use objects like bellow
var arr = {} //create a object
arr['a'] = 'val1'; //asign values
arr['b'] = 'val2';
arr['c'] = 'val3';
console.log(arr) //prints {a: "val1", b: "val2", c: "val3"}
delete arr['a'] //delete a key
console.log(arr) // prints {b: "val2", c: "val3"}
Well it does not work, because you are using an array as a dictionary, which it's not. First of all use object for that. Second use delete to remove a property:
var dict = { 'a': 'val1', 'b': 'val2', 'c': 'val3' };
delete dict.a;
As said before, this is not an Array. If it should be an array, it looks like this
var arr = ['val1', 'val2', 'val3'];
Now you can use Array.splice to remove value 'val2':
arr.splice(1,1);
// or
arr.splice(arr.indexOf('val2'),1);
// or even
arr = arr.filter(function (v){ return v !== 'val2'});
If it should be an object, its declariation looks like:
var obj = {a: 'val1', b: 'val2', c: 'val3'};
And if you want to delete 'val2' whilst not knowing the key for it you can loop:
for (var key in obj) {
if (obj[key] === 'val2';
delete obj[key];
}
// or (mis)use Object.keys
Object.keys(obj)
.filter(function(v){
return this[v] === 'val2' ? !(delete this[v]) : true;
}, obj);
Knowing this, you can create a helper method for Objects and Arrays:
function removeByValue(objOrArr, value) {
if (objOrArr instanceof Array && objOrArr.length) {
var found = objOrArr.indexOf(value);
if (found) { objOrArr.splice(found,1); }
}
if (objOrArr instanceof Object) {
var keys = Object.keys(objOrArr);
if (keys.length) {
keys.filter(function(v){
return this[v] === value ? !(delete this[v]) : true;
}, objOrArr);
}
}
return objOrArr;
}
// usage (using previous arr/obj)
removeByValue(arr, 'val2'); // arr now ['val1','val3']
removeByValue(obj, 'val2'); // obj now {a:'val1', c: 'val3'}
Example
_.difference([], [])
this method works fine when i'm having primitive type data like
var a = [1,2,3,4];
var b = [2,5,6];
and the _.difference(a,b) call returns [1,3,4]
but in case i'm using object like
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
doesn't seem to work
try this on for size for finding the difference of an array of objects:
var test = [{a: 1},{b: 2}];
var test2 = [{a: 1}];
_.filter(test, function(obj){ return !_.findWhere(test2, obj); });
While the accepted answer is correct, and the other answers give good ideas as well, there is an additional option that's pretty easy to implement with underscore.
This solution relies on each object having a unique ID, but in many cases this will be true, and you can get the difference of two arrays of objects in just two lines of code.
Using underscore's "pluck" method, you can quickly construct an array of all of the ID's in your source set and the target set. From there, all of underscore's array methods will work, difference, union, intersection etc...
After the operation, it is trivial to obtain the list of objects from your source list that you desire. Here's an example:
Verbose:
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
var arr1 = _.pluck(a, "id");
var arr2 = _.pluck(b, "id");
var diff = _.difference(arr1, arr2);
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });
or, more concisely:
var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id"));
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });
Of course, this same technique can be extended for use with any of the array methods.
Reason is simply that object with same content are not same objects e.g.
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
a.indexOf({'id':1, 'value':10})
It will not return 0 but -1 because we are searching for a different object
See the source code http://underscorejs.org/underscore.js, _.difference uses _.contains
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
and _.contains ultimately uses indexOf hence will not find objects unless they point to same object.
You can improve the underscore _.contains by looping through all items and calling a compare callback, which you should be able to pass to difference or contains function or you can check this version which improves contains methods
without using underscorejs,
here is the pretty simple method i got solution ...
a = [{'key':'123'},{'key':'222'},{'key':'333'}]
b = [{'key':'123'},{'key':'222'}]
var diff = a.filter(function(item1) {
for (var i in b) {
if (item1.key === b[i].key) { return false; }
};
return true;
});
console.log('result',diff)
I actually can imagine situations where I'd rather use #kontr0l approach than something else, but you have to understand that this approach is quadratic, so basically this code is an abstraction for naïve approach - iterate through all values in two arrays.
There are approaches better than quadratic, I won't use here any big O notation, but here are two main approaches, both are better then naïve one:
iterate through one of the arrays and check for existence in sorted second array using binary search.
put values into set/hash/dictionary/you name it.
As it've been already mentioned, first approach can be adopted for objects if you reimplement standard difference method with using some more flexible analogue of indexOf method.
With second approach we can hit the wall with the fact that, as of Feb'2015, only modern browsers are supporting Sets. As of hashes (well, objects) in javascript, they can have only string-type keys, so any object invoked as key first shoud be converted via toString method. So, we need to provide some => correspondece. On practice in most cases it's pretty straightforward, for instance, for your particular example such correspondence can be just String(obj.id).
Having such correspondence, we also can use following lodas/undercore approach:
var idsA = _.pluck(a, 'id');
var idsB = _.pluck(b, 'id');
// actually here we can stop in some cases, because
// quite often we need to identify object, but not the object itself -
// for instance to send some ids through remote API.
var intersect = _.intersection(idsA, idsB);
//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate:
var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find
var intersectObj = intersect.map(function(id) {return dictA[id})
But buy admitting slightly stricter restriction - that we can build correspondence between our set objects and natural numbers we can build even more efficent algorithm, i.e. all our ids are non-negative integers - we can use more efficient algorithm.
The trick is to implement set by introducing two helper arrays this way:
var naturalSet = function (arr) {
var sparse = [];
var dense = [];
var contains = function (i) {
var res = sparse[i] < dense.length && dense[sparse[i]] == i;
return res;
}
var add = function (v) {
if (!contains(v)) {
sparse[v] = dense.length;
dense.push(v);
}
}
arr.forEach(add);
return {
contains: contains,
toArray: function () {
return dense
},
_getDense: function () {
return dense
},
_getSparse: function () {
return sparse
}
}
}
Then we can introduce set with mapping to naturalSet:
var set = function (arr, valueOf) {
var natSet = naturalSet(arr.map(valueOf));
return {
contains: function (item) {
return natSet.contains(valueOf(item))
},
toArray: function () {
var sparse = natSet._getSparse();
var res = natSet._getDense().map(function (i) {
return arr[sparse[i]];
});
return res;
}
}
}
and finally, we can introduce intersection:
var intersection = function(arr1, arr2, valueOf) {
return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray();
}
So, relying on the structure of data you are working can help you sometimes.
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
var c = _.difference(a.map(e => e.id), b.map(e =>e.id));
var array = [];
array = a.map(e => {
if(c.includes(e.id)){
return e;
}
}).filter(r=>r);
Don't get why these answers are so complex unless I'm missing something?
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
// Or use lodash _.differenceBy
const difference = (array1, array2, prop = 'id') =>
array1.filter(item1 =>
!array2.some(item2 =>
item2[prop] === item1[prop],
),
);
// In one array.
console.log(difference(a, b));
// Intersection.
console.log([...difference(a, b), ...difference(b, a)]);
Forgive me for hopping in late here, but this may help:
array_of_objects =
// return the non-matching items (without the expected properties)
_.difference(array_of_objects,
// filter original list for items with expected properties
_.where(
// original list
array_of_objects,
// expected properties
{'id':1, 'value':10}
)
)
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);
}
}
}
}