lodash findwhere with empty array - arrays

I'm stuck with something appening when using lodash _.findWhere (the same with _.where)
var testdata = [
{
"id": "test1",
"arr": [{ "a" : "a" }]
},
{
"id": "test2",
"arr": []
}
];
_.findWhere(testdata, {arr : [] });
//--> both elements are found
I'm trying to extract elements from testdata where arr is an empty array, but _.where also includes elements with non-empty arrays.
I've also test with _.matchesProperty, but no way, same result.
I'm sure I'm missing something easy, but cannot see what :s
please help :)
http://plnkr.co/edit/DvmcsY0RFpccN2dEZtKn?p=preview

For this, you want to isEmpty():
var collection = [
{ id: 'test1', arr: [ { a : 'a' } ] },
{ id: 'test2', arr: [] }
];
_.find(collection, function(item) {
return _.isEmpty(item.arr);
});
// → { id: 'test2', arr: [] }
_.reject(collection, function(item) {
return _.isEmpty(item.arr);
});
// → [ { id: 'test1', arr: [ { a : 'a' } ] } ]
You can also use higher order functions, like flow(), so can abstract your callbacks:
var emptyArray = _.flow(_.property('arr'), _.isEmpty),
filledArray = _.negate(emptyArray);
_.filter(collection, emptyArray);
// → [ { id: 'test2', arr: [] } ]
_.filter(collection, filledArray);
// → [ { id: 'test1', arr: [ { a : 'a' } ] } ]

Related

Convert array of Objects into a grouped array of Objects Typescript

I'm trying to convert an array of objects like this:
[{grandParentField:'grandParent1', parentField:'parent1', childField: 'child1'},
{grandParentField:'grandParent1', parentField:'parent1', childField: 'child2'},
{grandParentField:'grandParent2', parentField:'parent1', childField: 'child3'},
{grandParentField:'grandParent2', parentField:'parent2', childField: 'child4'}]
into this form:
[
{
text: 'grandparent1',
items: [
{
text: 'parent1',
items: [{ text: 'child1' }, { text: 'child2' }]
}
]
},
{
text: 'grandparent2',
items: [
{
text: 'parent1',
items: [{ text: 'child3' }]
},
{
text: 'parent2',
items: [{ text: 'child4' }]
}
]
}
]
This Thread is similar to what I want, but not quite.
children will always be unique, but parents can have multiple grandparents.
Honestly I've tried so many things I'm not even sure which one to include as an example of what has gotten me closest.
Something like this but able to take in an array of Objects, and pump out the {text: string, items:[{text: string, items:[{text:string]]} structure:
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
console.log(groupBy(['one', 'two', 'three'], 'length'));
// => {3: ["one", "two"], 5: ["three"]}
Recursive approach, should work for every n-nested input that you will provide:
const input =[{grandParentField:"grandParent1",parentField:"parent1",childField:"child1"},{grandParentField:"grandParent1",parentField:"parent1",childField:"child2"},{grandParentField:"grandParent2",parentField:"parent1",childField:"child3"},{grandParentField:"grandParent2",parentField:"parent2",childField:"child4"}];
const nestedGroupBy = (nodes, order, orderIdx = 0) => {
const key = order[orderIdx]
let grouped = nodes.reduce((acc, e, i) => {
let node = acc.find(x => x.text == e[key])
if (!node) {
node = { text: e[key], items: [] }
acc.push(node)
}
node.items ? node.items.push(e) : node.items = [e]
return acc
}, [])
if (order[orderIdx + 1])
grouped = grouped.map(e => ({
text: e.text,
items: nestedGroupBy(e.items, order, orderIdx + 1)
}))
else
grouped = grouped.map(e => ({ text: e.text }) )
return grouped
}
const res = nestedGroupBy(input, Object.keys(input[0]))
console.log(res)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Without getting too crazy with types, I'd say that you want your output to be of this shape:
interface Tree {
text: string,
items?: Tree[]
}
So let's make a function called group() which takes your array and a list of keys that you want to process in the order they should be processed. So for your example it would be used like this:
const data = [
{ grandParentField: 'grandParent1', parentField: 'parent1', childField: 'child1' },
{ grandParentField: 'grandParent1', parentField: 'parent1', childField: 'child2' },
{ grandParentField: 'grandParent2', parentField: 'parent1', childField: 'child3' },
{ grandParentField: 'grandParent2', parentField: 'parent2', childField: 'child4' }
];
const groupedData = group(data, "grandParentField", "parentField", "childField");
Here's the implementation of group():
function group(data: Array<Record<string, string>>, key: string, ...otherKeys: string[]): Tree[] {
const objMap: Record<string, any[]> = {}
for (const d of data) {
if (!(d[key] in objMap)) {
objMap[d[key]] = []
}
objMap[d[key]].push(d);
}
return Object.keys(objMap).map(k => otherKeys.length ?
{
text: k,
items: group(objMap[k], otherKeys[0], ...otherKeys.slice(1))
} : {
text: k
}
);
}
First we group the elements from data into a dictionary of arrays called objMap, where each element d goes into the key of objMap at d[key] (so the first element goes into the key named "grandParent1" if key is "grandParentField").
Once this grouping is done, we return a new array by walking through objMap's keys. If we have no otherKeys, we just return an array of {text: string} elements using the keys of objMap as the text field. If we do have other keys, then we need to recursively call group() on the elements stored in objMap at the proper key.
You can verify that this works for your example:
console.log(JSON.stringify(groupedData, undefined, 2));
/* [
{
"text": "grandParent1",
"items": [
{
"text": "parent1",
"items": [
{
"text": "child1"
},
{
"text": "child2"
}
]
}
]
},
{
"text": "grandParent2",
"items": [
{
"text": "parent1",
"items": [
{
"text": "child3"
}
]
},
{
"text": "parent2",
"items": [
{
"text": "child4"
}
]
}
]
}
] */
Playground link to code

How to push data into an same object based on specific key in typescript

I have an jason result as below and I want to read from it and push to specific key as below
JSON result
[
{id:1,Name:"test",billNumber:"123"}
{id:2,Name:"test1",billNumber:"123"}
{id:3,Name:"test2",billNumber:"12345"}
{id:2,Name:"test3",billNumber:"12345"}
{id:3,Name:"test4",billNumber:"12334535"}
]
I want to have array list as below
{
"123":[{id:1,Name:"test",billNumber:"123"}, {id:2,Name:"test1",billNumber:"123"}],
"12345":[ {id:3,Name:"test2",billNumber:"12345"},{id:2,Name:"test3",billNumber:"12345"}],
"12334535":[{id:3,Name:"test4",billNumber:"12334535"}]
}
How to get the above list from the json result. Please do help
You don't need lodash to do that: just a regular Array.prototype.reduce will do the work. At each iteration, you simply check if the billNumber of the current item is in the object:
if it is not (i.e. a new entry), then you assign an array with a single element
if it is (i.e. the billNumber has been encountered before), then you simply push into the array
See proof-of-concept below:
const data = [{
id: 1,
Name: "test",
billNumber: "123"
}, {
id: 2,
Name: "test1",
billNumber: "123"
}, {
id: 3,
Name: "test2",
billNumber: "12345"
}, {
id: 2,
Name: "test3",
billNumber: "12345"
}, {
id: 3,
Name: "test4",
billNumber: "12334535"
}];
const transformedData = data.reduce((acc, cur) => {
if (cur.billNumber in acc) {
acc[cur.billNumber].push(cur);
} else {
acc[cur.billNumber] = [cur];
}
return acc;
}, {});
console.log(transformedData);
Use groupBy from lodash:
const result = groupBy(input, "billNumber")
(input is your array)
you can use reduce method.
[
{id:1,Name:"test",billNumber:"123"},
{id:2,Name:"test1",billNumber:"123"},
{id:3,Name:"test2",billNumber:"12345"},
{id:2,Name:"test3",billNumber:"12345"},
{id:3,Name:"test4",billNumber:"12334535"},
].reduce((acc, value) => {
if (!acc[value.billNumber]) {
acc[value.billNumber] = [];
}
acc[value.billNumber].push(value);
return acc;
}, {})
Here is the mimic code You cann use and get help
var a = [{a:2},{a:3},{a:4}]
let b = {}
let c = 1
a.forEach(obj => {
b[c] = [obj]
c++
})
output will be
{
1: [ { a: 2 } ],
2: [ { a: 3 } ],
3: [ { a: 4 } ]
}
Thanks I hope it will help !

Iterate through an array to match result

I have an array where any of the elements could have a match with a mongodb collection.
var elementsArray = ["item1", "item2", "item3"];
db.getCollection("myCollections").aggregate([
...
{ $group: {...} },
{
$match: {
for(item in elementsArray ){ // this for loop is not appropriate but to give the idea
$or:[
{ "word": item },
{ "preferredWord": item },
{ "synonym": item }
],
}
}
}
])
I know the for loop is inappropriate here but how can iterate through the elementsArray to find a match with any of the keywords (word, preferredWord, or synonym) that can perform the similar operations if the for loop was allowed like this?
Thanks
You can run your array outside of Aggregate, by just creating an array and including it inside of your Aggregate query:
var elementsArray = ["item1", "item2", "item3"];
var orArray = [];
for (item in elementsArray ) {
orArray.push( { "word": elementsArray[item], "preferredWord": elementsArray[item], "synonym": elementsArray[item] } );
}
Then you can use orArray object inside your aggregate:
db.getCollection("myCollections").aggregate([
...
{ $group: {...} },
{
{
$match: {
"$or" : orArray
}
}
]);
You can use the $in aggregation operator to match a field with elements from the array. You can add the other aggregation stages where appropriate (I cannot specify the entire query without the definition of the collection's input document)
Example matches collection:
{ _id: 1, fld1: "blah", word: "item2", synonym: "item3" }
{ _id: 2, fld1: "zzz", word: "item1", synonym: "item2" }
{ _id: 3, fld1: "123", word: "item99", synonym: "item99" }
The Query (from mogo shell):
var arr = [ "item1", "item2" ];
db.matches.aggregate( [
{
$addFields: {
"hasMatches": { $or: [ {$in: [ "$word", arr ]}, {$in: [ "$synonym", arr ]} ] }
}
},
{
$match: { hasMatches: true }
}
] )
The Output:
{ "_id" : 1, "fld1" : "blah", "word" : "item2", "synonym" : "item3", "hasMatches" : true }
{ "_id" : 2, "fld1" : "zzz", "word" : "item1", "synonym" : "item2", "hasMatches" : true }

Remove object from nested array if array is empty in Typescript

How can I remove the object if the nested array is empty. Like I have an array:
pokemonGroups = [
{
name: 'Grass',
pokemon: [
'bulbasaur-0', 'Bulbasaur', 'oddish-1','Oddish','bellsprout-2', 'Bellsprout'
]
},
{
name: 'Water',
pokemon: [
]
}]
So In this we have an empty array
{
name: 'Water',
pokemon: []
}
So I want to remove this object and my array should be like:
pokemonGroups = [
{
name: 'Grass',
pokemon: [
'bulbasaur-0', 'Bulbasaur', 'oddish-1','Oddish','bellsprout-2', 'Bellsprout'
]
}
]
You can use filter:
pokemonGroups = pokemonGroups.filter(group => group.pokemon.length != 0);
You can iterate your array and use array.splice()
var pokemonGroups = [{
name: 'Grass',
pokemon: [
'bulbasaur-0', 'Bulbasaur', 'oddish-1', 'Oddish', 'bellsprout-2', 'Bellsprout'
]
},
{
name: 'Water',
pokemon: [
]
}
]
for (var i = 0; i < pokemonGroups.length; i++) {
if (pokemonGroups[i]['pokemon'].length == 0) {
pokemonGroups.splice(i, 1);
}
}
console.log(pokemonGroups)

Mongodb array maching

I have the following document:
{ arr : [1,2,3] }
And I have to compare it with the following:
a : [1,2]
b : [2,3,1]
c : [2,5,3,1]
I need to make a query that return arr only when it match all the elements of the query array.
In the example, it would be "b" and "c"
I have tried with $all like the following:
find(arr:{$all:a}) (the same for b and c)
but this does not work because it match "a" too. :(
Try this:
find( {$or:[
{ $and: [ { arr: 1 }, { arr: 2 }, {arr:{$size:2}} ] },
{ $and: [ { arr: 1 }, { arr: 2 }, { arr: 3 }, {arr:{$size:3}} ] },
{ $and: [ { arr: 1 }, { arr: 2 }, { arr: 3 }, { arr: 5 }, {arr:{$size:4}} ] }
]})
or maybe with the $all operator (i haven't tryied that before but should be ok):
find( {$or:[
{ $and: [ { arr: { $all: a } }, {arr:{$size: a.length }} ] },
{ $and: [ { arr: { $all: b } }, {arr:{$size: b.length }} ] },
{ $and: [ { arr: { $all: c } }, {arr:{$size: c.length }} ] }
]})
You can use the aggregation framework and its set operations to select arrays which are subsets of the query array:
> db.foo.insert({arr:[1,2,3]})
> db.foo.insert({arr:[3,2,1]})
> db.foo.insert({arr:[3,2,1,4]})
and then
db.foo.aggregate([
{ $project: { arr:'$arr', isIn: { $setIsSubset: ['$arr', [2,5,3,1]] }}},
{ $match: { isIn: true }}
])
returns
{ "_id" : ObjectId("5345beb536cdafd3eb4a6b16"), "arr" : [ 1, 2, 3 ], "isIn" : true }
{ "_id" : ObjectId("5345bec036cdafd3eb4a6b17"), "arr" : [ 3, 2, 1 ], "isIn" : true }
Note that this requires version 2.6+

Resources