Add array element during definition based on condition - javascript

I define an array like this:
[{foo:0}, true === false && { foobar:1}, {bar:2}]
My expected result would be that the middle item is not added at all when the middle condition is not met:
[ { foo: 0 }, { bar: 2 } ]
in fact it adds false as an array item:
[ { foo: 0 }, false, { bar: 2 } ]
Is there a way to prevent adding the false while maintaining this lightweight syntax (I know I could always use push or the spread operator)

You could use concat with spread syntax and an empty array as neutral value.
var a = [].concat(...[
{ foo: 0 },
true === false ? { foobar: 1 } : [],
{ bar: 2 }
]);
console.log(a);
With apply
var a = Array.prototype.concat.apply([], [
{ foo: 0 },
true === false ? { foobar: 1 } : [],
{ bar: 2 }
]);
console.log(a);

As Denys suggested, you could do this:
const arr = [{foo:0}, true === false && { foobar:1}, {bar:2}].filter(el => el !== false);
console.log(arr);

Related

Order By custom order in object inside property

I need to sort an array of objects by a property of a sub-object:
foo = [
{
bar: {
order: "hello"
}
},
{
bar: {
order: "something"
}
},
{
bar: {
order: "else"
}
},
]
If I want the order of the foo objects to based on a custom order (not alphabetical!) set by order values like
{ "something": 1, "hello": 2, "else": 3 }
with something like _orderBy(foo, indexOfMyCustomOrder, 'desc'), how can I achieve this? Or do I need to separate this logic into two functions?
Define indexOfMyCustomOrder as follows:
const indexOfMyCustomOrder = o => order[o.bar.order];
... where the order variable should be the object that defines the sequence for each possible value of the order property.
See snippet:
const foo = [{bar:{order:"hello"}},{bar:{order: "something"}},{bar:{order:"else"}}];
const order = { "something": 1, "hello": 2, "else": 3 };
const result = _.orderBy(foo, o => order[o.bar.order]);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script>
You can do it like this:
const foo = [{ bar: { order: "hello" } }, { bar: { order: "something" } }, { bar: { order: "else" } } ]
let order = { something: 0, hello: 1, ["else"]: 2 }
console.log(_.orderBy(foo, x => order[x.bar.order]))
console.log(_.orderBy(foo, x => order[x.bar.order], 'desc'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Where you defined your order by index map and use that to filter the data.
You can do this without lodash using a function, where you get the keys of your custom order, you sort them based on their value and the mode (desc or asc) and then reduce that, filtering the array you want ordered adding each time the elements. Hope this helps.
const foo = [{
bar: {
order: "hello"
}
},{
bar: {
order: "something"
}
},{
bar: {
order: "else"
}
},{
bar: {
order: "else"
}
},{
bar: {
order: "anotherelse"
}
}];
const order = { "something": 1, "hello": 3, "else": 2, "missing": 4, "anotherelse": 2 };
const orderWith = (array, order, desc) =>
Object.keys(order)
.sort((a, b) => desc ? order[b] - order[a] : order[a] - order[b])
.reduce((acc, val) =>
acc.concat(array.filter(({ bar: { order }}) => val === order))
, []);
console.log('Descending order');
console.log(orderWith(foo, order));
console.log('Ascending order')
console.log(orderWith(foo, order, true));

Map through an inner array of an Object

I have this object:
let arr = [{
id : 1,
usr : 'pimba',
xyz: null
},
{
id : 2,
usr : 'aloha',
xyz: {
xyz_id: 2
}
},
{
id : 3,
age : 'pruu',
xyz: null
}];
As you can notice, sometimes xyz is null and sometimes it's not.
I need to recognize whether it is null or not, so I can read it.
I was trying to use map() function but I can't set some sort of filter to only execute the annonymous function when it is NOT null.
I managed to do something like this:
let result = Object.values(arr).map(function(row){
if(row['xyz'] != null) {
console.log(row['xyz_id']);
}
});
what If I want a new array containing ONLY xyz_id ? Is there a shorter version ?
Second case:
There are more than 1 value inside xyz and it's NOT "named".
let arr = [{
id : 1,
usr : 'pimba',
xyz: null
},
{
id : 2,
usr : 'aloha',
xyz: {
xyz_id: {"value1Here", "Value2Here"}
}
},
{
id : 3,
age : 'pruu',
xyz: null
}];
It seems you want to map the array only for the elements that have not-null xyz property. One option is using both .filter and .map methods. Another option is using the .reduce method:
let result = arr.reduce(function(ret, row) {
// assuming `xyz` can be only `null` or an object
if ( row.xyz !== null ) {
ret.push(row.xyz.xyz_id);
}
return ret;
}, []);
You might want to look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
const notNull = arr.filter(elm => elm.xyz !== null);
var a = {one: 1, two: null, three: 3, four: true}
var y = []
let scan = (obj) => {
Object.keys(obj).forEach(x => {
if (obj[x] === null) {
console.log('Its null')
} else {
// Extend here to datatypes
y.push(obj[x])
}
});
}
scan(a)
console.log(y)

lodash - filter function using an array of filters objects

I wonder how I could use lodash to filter some objects using an array of filters options.
So far, I have a function that can use the array of filters option but I can't manage to take the LOGICAL operator (AND, OR) into consideration.
Edit:
we find those objects which match every LOGICAL : AND condition or any LOGICAL : OR condition
See this demo:
var toFilter = [
{"foo" : 1, "bar": 0},
{"foo" : 1, "bar": 0},
{"foo" : 2, "bar": null}
];
var filters = [
{"KEY": "foo", "VALUE": 1, "EQUALITY": "EQUAL", "LOGICAL": "OR"},
{"KEY": "bar", "VALUE": 0, "EQUALITY": "NOT_EQUAL", "LOGICAL": "AND"}
];
//the function should be equivalent to this one:
_.filter(toFilter, function(obj) { return obj.foo == 1 || obj.bar != 0 });
//this function does not take the LOGICAL operator ("AND" and "OR") in consideration
_.uniq(_.flatMap(filters, function(filter) {
return _.filter(toFilter, function(obj) {
if (filter.EQUALITY == "EQUAL") {
return obj[filter.KEY] == filter.VALUE;
} else {
return obj[filter.KEY] != filter.VALUE;
}
});
}));
This is a very simple example, but the list of filters could be a lot larger.
I think what you're looking for is Array.prototype.every and Array.prototype.some. This might do what you need. If there are actual duplicates you need to remove, then lodash's uniq could be added easily enough.
const conditions = {
'EQUAL': (a, b) => a === b,
'NOT_EQUAL': (a, b) => a !== b,
}
const check = (objs, filters) => objs.filter(obj => {
const ands = filters.filter(filter => filter.LOGICAL === 'AND')
const ors = filters.filter(filter => filter.LOGICAL === 'OR')
return ors.some(
filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)
) || (ands.length && ands.every(
filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)
))
})
const toFilter = [
{foo : 1, bar: 0, id: 'a'},
{foo : 1, bar: 0, id: 'b'},
{foo : 2, bar: null, id: 'c'},
{foo : 2, bar: 0, id: 'd'},
];
const filters = [
{KEY: "foo", VALUE: 1, EQUALITY: "EQUAL", LOGICAL: "OR"},
{KEY: "bar", VALUE: 0, EQUALITY: "NOT_EQUAL", LOGICAL: "AND"}
];
console.log(check(toFilter, filters)) //=> ids a, b and c
Note that this design makes it easy to add new conditions. For instance:
'GREATER_THAN': (a, b) => a > b
If this is for the web, and you don't have a build process, I would recommend storing the conditions in a local closure for the function, with something like this (untested):
const check = (() => {
const conditions = {
'EQUAL': (a, b) => a === b,
'NOT_EQUAL': (a, b) => a !== b,
}
return (objs, filters) => (objs, filters) => objs.filter(obj => {
const ands = filters.filter(filter => filter.LOGICAL === 'AND')
const ors = filters.filter(filter => filter.LOGICAL === 'OR')
return ors.some(
filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)
) || (ands.length && ands.every(
filter => conditions[filter.EQUALITY](obj[filter.KEY], filter.VALUE)
))
})
})()
This will be my approach to solve the problem:
var toFilter = [{ foo: 1, bar: 0 }, { foo: 1, bar: 0 }, { foo: 2, bar: null }];
var filters = [
{ KEY: 'foo', VALUE: 1, EQUALITY: 'EQUAL', LOGICAL: 'OR' },
{ KEY: 'bar', VALUE: 0, EQUALITY: 'NOT_EQUAL', LOGICAL: 'AND' }
];
var orFilters = _.filter(filters, ['LOGICAL', 'OR']);
var andFilters = _.filter(filters, ['LOGICAL', 'AND']);
var result = _.filter(toFilter, evalFilters);
function evalFilters(obj) {
var curriedEquality = equality(obj);
return (
_.some(orFilters, curriedEquality) ||
(andFilters.length && _.every(andFilters, curriedEquality))
);
}
function equality(obj) {
return function(filter) {
return filter.EQUALITY === 'EQUAL'
? obj[filter.KEY] === filter.VALUE
: obj[filter.KEY] !== filter.VALUE;
};
}
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
The first step is to separate the OR filters and the AND filters in two different arrays.
Then I would iterate over the elements to be filtered, I would make a curried function equality which will receive the element to be evaluated and returns a function which contains the equality comparison.
Then I would use _.some to check if the element evals to true in at least one OR filter if not, I would use _.every to verify if the element evals to true in all the AND filters.
Obs: Is necessary to verify if the andFilters array is not empty, because _.every and also Array.prototype.every will return true if it's the case.
Hope this explanation helps!

Remove duplicate object from array javascript [duplicate]

I have this kind of array:
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
I'd like to filter it to have:
var bar = [ { "a" : "1" }, { "b" : "2" }];
I tried using _.uniq, but I guess because { "a" : "1" } is not equal to itself, it doesn't work. Is there any way to provide underscore uniq with an overriden equals function?
.uniq/.unique accepts a callback
var list = [{a:1,b:5},{a:1,c:5},{a:2},{a:3},{a:4},{a:3},{a:2}];
var uniqueList = _.uniq(list, function(item, key, a) {
return item.a;
});
// uniqueList = [Object {a=1, b=5}, Object {a=2}, Object {a=3}, Object {a=4}]
Notes:
Callback return value used for comparison
First comparison object with unique return value used as unique
underscorejs.org demonstrates no callback usage
lodash.com shows usage
Another example :
using the callback to extract car makes, colors from a list
If you're looking to remove duplicates based on an id you could do something like this:
var res = [
{id: 1, content: 'heeey'},
{id: 2, content: 'woah'},
{id: 1, content:'foo'},
{id: 1, content: 'heeey'},
];
var uniques = _.map(_.groupBy(res,function(doc){
return doc.id;
}),function(grouped){
return grouped[0];
});
//uniques
//[{id: 1, content: 'heeey'},{id: 2, content: 'woah'}]
Implementation of Shiplu's answer.
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var x = _.uniq( _.collect( foo, function( x ){
return JSON.stringify( x );
}));
console.log( x ); // returns [ { "a" : "1" }, { "b" : "2" } ]
When I have an attribute id, this is my preffered way in underscore:
var x = [{i:2}, {i:2, x:42}, {i:4}, {i:3}];
_.chain(x).indexBy("i").values().value();
// > [{i:2, x:42}, {i:4}, {i:3}]
Using underscore unique lib following is working for me, I m making list unique on the based of _id then returning String value of _id:
var uniqueEntities = _.uniq(entities, function (item, key, a) {
return item._id.toString();
});
Here is a simple solution, which uses a deep object comparison to check for duplicates (without resorting to converting to JSON, which is inefficient and hacky)
var newArr = _.filter(oldArr, function (element, index) {
// tests if the element has a duplicate in the rest of the array
for(index += 1; index < oldArr.length; index += 1) {
if (_.isEqual(element, oldArr[index])) {
return false;
}
}
return true;
});
It filters out all elements if they have a duplicate later in the array - such that the last duplicate element is kept.
The testing for a duplicate uses _.isEqual which performs an optimised deep comparison between the two objects see the underscore isEqual documentation for more info.
edit: updated to use _.filter which is a cleaner approach
The lodash 4.6.1 docs have this as an example for object key equality:
_.uniqWith(objects, _.isEqual);
https://lodash.com/docs#uniqWith
Try iterator function
For example you can return first element
x = [['a',1],['b',2],['a',1]]
_.uniq(x,false,function(i){
return i[0] //'a','b'
})
=> [['a',1],['b',2]]
here's my solution (coffeescript) :
_.mixin
deepUniq: (coll) ->
result = []
remove_first_el_duplicates = (coll2) ->
rest = _.rest(coll2)
first = _.first(coll2)
result.push first
equalsFirst = (el) -> _.isEqual(el,first)
newColl = _.reject rest, equalsFirst
unless _.isEmpty newColl
remove_first_el_duplicates newColl
remove_first_el_duplicates(coll)
result
example:
_.deepUniq([ {a:1,b:12}, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ],[ 2, 1, 2, 1 ], {a:1,b:12} ])
//=> [ { a: 1, b: 12 }, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ] ]
with underscore i had to use String() in the iteratee function
function isUniq(item) {
return String(item.user);
}
var myUniqArray = _.uniq(myArray, isUniq);
I wanted to solve this simple solution in a straightforward way of writing, with a little bit of a pain of computational expenses... but isn't it a trivial solution with a minimum variable definition, is it?
function uniq(ArrayObjects){
var out = []
ArrayObjects.map(obj => {
if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
})
return out
}
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);
Lets break this down. First lets group the array items by their stringified value
var grouped = _.groupBy(foo, function (f) {
return JSON.stringify(f);
});
grouped looks like:
{
'{ "a" : "1" }' = [ { "a" : "1" } { "a" : "1" } ],
'{ "b" : "2" }' = [ { "b" : "2" } ]
}
Then lets grab the first element from each group
var bar = _.map(grouped, function(gr)
return gr[0];
});
bar looks like:
[ { "a" : "1" }, { "b" : "2" } ]
Put it all together:
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) {
return JSON.stringify(f);
}), function (gr) {
return gr[0];
}
);
You can do it in a shorthand as:
_.uniq(foo, 'a')

Merge two arrays with objects

I plan to merge two objects:
var c = {
name: "doo",
arr: [
{
id: 1,
ver: 1
},
{
id: 3,
ver: 3
}
]
};
var b = {
name: "moo",
arr: [
{
id: 1,
ver: 0
},
{
id: 2,
ver: 0
}
]
};
When using Object.assign({},b,c) what happens is, that the b.arr is simply being replaced with c.arr.
My question is, how do I preserve objects inside the b.arr that are not in c.arr but still merge objects from that array when they match b.arr[0].id === c.arr[0].id. The desired outcome would look like:
{
name: "doo",
arr: [
{
id: 1,
ver: 1
},
{
id: 2,
ver: 0
},
{
id: 3,
ver: 3
}
]
}
Thanks.
You could have a look at ArrayUtils.addAll() from the apache commons
As soon as you use lodash - you may use a combination of lodash's functions. It may look a bit complex but it's not:
_.assign({}, b, c, function(objectValue, sourceValue, key, object, source) {
//merging array - custom logic
if (_.isArray(sourceValue)) {
//if the property isn't set yet - copy sourceValue
if (typeof objectValue == 'undefined') {
return sourceValue.slice();
} else if (_.isArray(objectValue)) {
//if array already exists - merge 2 arrays
_.forEach(sourceValue, function(sourceArrayItem) {
//find object with the same ID's
var objectArrayItem = _.find(objectValue, {id: sourceArrayItem.id});
if (objectArrayItem) {
//merge objects
_.assign(objectArrayItem, sourceArrayItem);
} else {
objectValue.push(sourceArrayItem);
}
});
return objectValue;
}
}
//if sourceValue isn't array - simply use it
return sourceValue;
});
See the full demo here.
Try this function:
function mergeArrayObjects (a, b) {
var tmp, // Temporary array that will be returned
// Cache values
i = 0,
max = 0;
// Check if a is an array
if ( typeof a !== 'object' || typeof a.indexOf === 'undefined')
return false;
// Check if b is an array
if ( typeof b !== 'object' || typeof b.indexOf === 'undefined')
return false;
// Populate tmp with a
tmp = a;
// For each item in b, check if a already has it. If not, add it.
for (i = 0, max = b.length; i < max; i++) {
if (tmp.indexOf(b[i]) === -1)
tmp.push(b[i]);
}
// Return the array
return tmp;
}
JsFiddle here
Note: Because I'm anal, I decided to see if this function is faster than the alternative proposed. It is.
Using lodash, I would do something like this:
var first = {
name: 'doo',
arr: [
{ id: 1, ver: 1 },
{ id: 3, ver: 3 }
]
};
var second = {
name: 'moo',
arr: [
{ id: 1, ver: 0 },
{ id: 2, ver: 0 }
]
};
_.merge(first, second, function(a, b) {
if (_.isArray(a)) {
return _.uniq(_.union(a, b), 'id');
} else {
return a;
}
});
// →
// {
// name: 'doo',
// arr: [
// { id: 1, ver: 1 },
// { id: 2, ver: 0 },
// { id: 3, ver: 3 }
// ]
// }
The merge() function let's you specify a customizer callback for things like arrays. So we just need to check it it's an array we're dealing with, and if so, use the uniq() and union() functions to find the unique values by the id property.

Categories

Resources