I want to remove elements from an array when they match a certain regular expression.
Here is the Lodash documentation on the pull() method.
I want a result similar to this.
const array = ['a', 'b', 'c', 'a', 'b', 'c'];
_.pull(array, 'b', 'c',);
console.log(array); // [ 'a', 'a', ]
Only, I want to use regex instead of strings. But this does not achieve that result.
const array = ['a', 'b', 'c', 'a', 'b', 'c'];
const re = /(b|c)/gm
_.pull(array, re,);
console.log(array); // ["a", "b", "c", "a", "b", "c"]
What am I doing wrong?
The _.pull() method doesn't accept a predicate, use _.remove():
Removes all elements from array that predicate returns truthy for
const array = ['a', 'b', 'c', 'a', 'b', 'c'];
const re = /b|c/
_.remove(array, c => re.test(c));
console.log(array); // ["a", "a"]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
If you're interested in a vanilla JS solution, see below for using RegExp test!
let array = ['a', 'b', 'c', 'a', 'b', 'c'];
const re = /(b|c)/
array = array.filter(el => !re.test(el));
console.log(array);
Related
How to easily assert nested array elements with another array values? I tried the following but it's not working as expected.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(element => console.log(expectedCells.includes(element)));
//Expected: true, true, false, true but it returns false, false, false, false
You can use Array#some to check if any of the array elements are in the other array.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(element => console.log(element.some(x => expectedCells.includes(x))));
expectedCells.includes(element) checks, if a nested array of actual is in the expectedCells array, which only contains strings, so the output is always false
What you need to do is to also iterate over the elements of the nested array.
var expectedCells = ['a', 'b', 'c', 'd']
var actual = [['a'], ['b', 'e'], ['e'], ['c']] //['b', 'e'] should also return true
actual.forEach(elements => {
for (let i=0; i<elements.length; i++) {
if (expectedCells.includes(elements[i])) {
return console.log(true)
}
return console.log(false)
}
});
There's a couple of things going on here.
First, you aren't comparing the elements inside of actual's subarrays. You're comparing the subarrays themselves.
actual.forEach(...) is executing something like this:
console.log(expectedCells.includes(['a']))
console.log(expectedCells.includes(['b', 'e']))
console.log(expectedCells.includes(['e']))
console.log(expectedCells.includes(['c']))
None of the array objects will match the letters a, b, c, or d from the expectedCells array.
You'll have to create a double loop:
actual.forEach(
elementArray =>
console.log(
elementArray.some(element => expected.includes(element))
)
)
I like to use Array.prototype.reduce() for several different scenarios in my code, it's pretty straightforward and handy.
Please observe in the 2 different solutions below the reduce() function takes 2 different initial values, in the first case is new Set() and the second is [].
In the below example the code uses reduce() without return keyword - Set one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.add(c), new Set());
console.log(Array.from(result));
The next example is using still reduce() but here with a return keyword - Array one:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => {
a.find(e => e === c) ? null : a.push(c);
return a;
}, []);
console.log(result);
Question:
So the .add() function for Set returns the Set object itself. The .push() function for Array returns the length of the used Array.
The Set case helps me to shorten the code using .reduce() without return keyword because the above mentioned reason. In my second example I would like to use the solution without return keyword but still with Array somehow.
Is there any workaround solution to get the same result but without using return keyword in the second example? I would like to shorten the code further if possible.
Any help is appreciated.
You could take either the accumulator, if found or concat the element to the accumulator.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.find(e => e === c) ? a : a.concat(c), []);
console.log(result);
Just to mention, Set takes a complete array with the constructor.
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = Array.from(new Set(data));
console.log(result);
Array#concat can add a new item to an array and returns a new array, so can works similar to Set#add. However, it still needs the conditional operator since you want to either add an element or nothing - for the latter case that's concatenating an array with an empty array:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => a.concat(a.some(e => e === c) ? [] : c), []);
console.log(result);
Alternatively, you can use spread syntax to again combine two arrays:
const data = ['a', 'b', 'c', 'd', 'a', 'k', 'b'];
const result = data.reduce((a, c) => [...a, ...(a.some(e => e === c) ? [] : c)], []);
console.log(result);
Neither of the two is perfect, to be honest. The existence of the conditional operator makes this harder to read when all one line but it's still an option.
I have an array:
arr = ["a", "b", c"]
I want to double values like
arr = ["a", "b", c", "a", "b", "c"]
(not in particular order).
What's the best way to do it in JavaScript?
Possible solution, using Array#concat.
var arr = ["a", "b", "c"],
res = arr.concat(arr);
console.log(res);
You could also use ES6 spread syntax with Array.push:
let arr = ['a', 'b', 'c'];
arr.push(...arr);
console.log(arr);
You can use Array() constructor and inside specify number of times you want to repeat array and then use fill() and spread syntax to create one array.
var arr = ["a", "b", "c"];
var newArr = [].concat(...Array(3).fill(arr))
console.log(newArr)
A for-loop solution which mutates the array itself.
var arr=["a","b","c"];
var len=arr.length;
for(var i=len;i<2*len;i++) arr[i]=arr[i-len]
console.log(arr);
What I'd like to do is compare 2 arrays of primitives using chai.js, but without considering the order of elements - like if they were 2 sets.
Now obviously I can do something like this:
const actual = ['a', 'b', 'c'];
const expected = ['b', 'c', 'a'];
expect(actual).to.have.length(expected.length);
expected.forEach(e => expect(actual).to.include(e));
But I'm curious if there is a prefered 'built in' way of doing this (I couldn't find it in the docs).
You can use the built in check 'members':
expect([4, 2]).to.have.members([2, 4])
Answers above expect([4, 2]).to.have.members([2, 4]) won't check the size as author mentioned about.
expect([1,2,2]).to.have.members([1,2]) will pass the test.
The best option is to use https://www.chaijs.com/plugins/deep-equal-in-any-order/
You can use the members assertion from chai BDD.
var mocha = require('mocha');
var should = require('chai').should();
let a = ['a', 'b', 'c'];
let b = ['c', 'a', 'b'];
let c = ['d', 'e', 'c', 'b'];
describe('test members', function() {
it('should pass', function() {
a.should.have.members(b);
});
it('should fail', function() {
a.should.not.have.members(c);
});
});
I'm basically using $all operator in mongo and the input i get might be array or single element as following. So how do use underscore to put all the elements in one array and then
userId = 'a';
userIds = ['a', 'b', 'c'];
otherId = ['a'] or 'a';
searchArray = [userId, userIds, otherId]
db.collections.find({userIds: {$all: searchArray}})
You can use union as long as they are arrays.
_.union(arrays)
var userId = ['a'],
userIds = ['a', 'b', 'c'];
otherId = ['a'],
searchArray = _.union(userId, userIds, otherId);
If all variables aren't promised to be arrays, you probably want the flatten method.
userId = 'a'; // strings
userIds = ['a', 'b', ['c']]; // multidimensional array
otherId = ['a']; // single dimensional array
searchArray = _.flatten([userId, userIds, otherId]);
db.collections.find({userIds: {$all: searchArray}})
No need for underscore, you can use concat:
var userId = ['a'],
userIds = ['a', 'b', 'c'],
otherId = ['a'];
var arr = userId.concat(userIds, otherId)
This will work even if one of those is not an array but just a number or string. Working example here:
http://codepen.io/anon/pen/qbNQLw