Lodash iterate nested loops - javascript

I am trying to iterate through below Collections JSON object. I am trying to find collection elements which have one of the tags from tagArray. Basically this is a filter exercise to have collection elements that have tags as selected from the tagArray.
{
1: {
"description": "AAA",
"tags": [
{
"name": "tag1",
},
{
"name": "tag2",
},
{
"name": "tag3",
},
],
"name": "XYZ",
},
2: {
"description": "BBB",
"tags": [
{
"name": "tag1",
}
],
"name": "CCC",
},
3: {
"description": "xms",
"tags": [],
"name": "Huo",
},
4: {
"description": "asd",
"tags": [],
"name": "TXS",
}
}
tagArray looks like this : [ tag1, tag2, ... ]
I have coded it as below using lodash and it works fine. But I am not sure if I can improve this further and how?
const filterByTags = (collections, filterTags) => {
let filteredCollections = _.pickBy(collections, (collection) => {
let collectionWithTag = false;
_.map(collection.tags, (collectionTag) => {
if (filterTags.indexOf(collectionTag.name) !== -1) {
collectionWithTag = true;
return collectionWithTag;
}
});
return collectionWithTag;
});
return filteredCollections;
};

You don't want to use pickBy but rather filter (Lodash/native)
You don't want to use map but rather some (Lodash/native)
You don't want to use indexOf but rather includes (Lodash/native)
function filterByTags(collections, filterTags) {
return _.filter(collections, collection => {
return _.some(collection.tags, collectionTag => {
return _.includes(filterTags, collectionTag.name);
});
});
}

Related

If the 'id' key is duplicated among the objects in the array, how to delete the object [duplicate]

This question already has answers here:
How to remove all duplicates from an array of objects?
(77 answers)
Closed 4 months ago.
If the 'id' key is duplicated among the objects in the array, how to delete the object
I tried using filter, map, and set, but it doesn't work.
It's not a one-dimensional array, so I don't know how to do it.
as-is
"category": {
"key": 1,
"order": 1,
"list": [
{
"id": "12345",
...
},
{
"id": "12345",
...
},
{
"id": "67890",
...
},
]
}
to-be
"category": {
"key": 1,
"order": 1,
"list": [
{
"id": "12345",
...
},
{
"id": "67890",
...
},
]
}
We iterate over that list using reduce function, then we checked whether the key we are accessing is visited or not with keys parameter of reduce method, and if it's not visited then we just push that object to a filtered array and returning keys array to keep it updated.
const data = {
"category": {
"key": 1,
"order": 1,
"list": [{
"id": "12345"
},
{
"id": "12345"
},
{
"id": "67890"
},
]
}
}
let filtered = [];
data.category.list.reduce((keys, currentObject) => {
if (!keys.includes(currentObject.id)) { //checking if current oject id is present in keys or not
// if not present than we will just push that object in
keys.push(currentObject.id);
//getting filttered object
filtered.push(currentObject);
}
return keys; //returning keys to update it
}, [])
data.category.list = filtered; //updating list
console.log(data);
A solution based on #Nick's comment
let data ={
"category": {
"key": 1,
"order": 1,
"list": [
{
"id": "12345"
},
{
"id": "12345"
},
{
"id": "67890"
},
]
}
}
let uniq = data.category.list.filter((o,i,a) => a.findIndex(o2 => o2.id == o.id) == i)
data.category.list = uniq
console.log(data)
You can use a set to track if id
const category = [{
"category": {
"key": 1,
"order": 1,
"list": [{
"id": "12345",
},
{
"id": "12345",
},
{
"id": "67890",
},
]
}
}]
const z = category.map(elem => {
const set = new Set()
return {
...elem,
category: {
...elem.category,
list: elem.category.list.reduce((acc, curr) => {
if (!set.has(curr.id)) {
set.add(curr.id);
acc.push(curr)
}
return acc;
}, [])
}
}
});
console.log(z)

JavaScript: filter array of objects by another

I'm trying to filter some objects based on another array of objects. So I'm getting data from an API. These are for example receipts:
[
{
"id": 1,
"name": "test",
"category": {
"id": 1,
"name": "Cookies",
},
},
{
"id": 2,
"name": "test2",
"category": {
"id": 2,
"name": "Candy",
},
}
]
Then I'm trying to filter the objects on the category name based on another array of categories.
I've created a function for this:
function onSelectCategory(category) {
let receiptsList = receipts.filter((a) =>
a.category.includes(category.name)
);
setReceiptsView(receiptsList);
setSelectedCategory(category);
}
const category = [ { "id": 2, "name": "Candy" } ];
onSelectCategory(category);
When I run this function, I get an empty Array []. I can't really figure out what I'm doing wrong.
Since the param seems to be an array of objects, you need to use Array#some for comparison instead:
const receipts = [
{ "id": 1, "name": "test", "category": { "id": 1, "name": "Cookies" } },
{ "id": 2, "name": "test2", "category": { "id": 2, "name": "Candy" } }
];
const categories = [ { "id": 2, "name": "Candy" } ];
const receiptsList = receipts.filter(({ category }) =>
categories.some(({ name }) => name === category.name)
);
console.log(receiptsList);
Another solution using Set:
const receipts = [
{ "id": 1, "name": "test", "category": { "id": 1, "name": "Cookies" } },
{ "id": 2, "name": "test2", "category": { "id": 2, "name": "Candy" } }
];
const categories = [ { "id": 2, "name": "Candy" } ];
const categorySet = new Set(categories.map(({ name }) => name));
const receiptsList = receipts.filter(({ category }) =>
categorySet.has(category.name)
);
console.log(receiptsList);
Assuming that category (the parameter) is a string, the issue is that you are attempting to get the attribute name from the string, when you should be comparing the string to the object.
Try this:
a.category.name == category;
instead of
a.category.includes(category.name)
I may be wrong aboout assuming that category is a string, please clarify by telling us what the parameter category is equal to.

Group nested arrays in an object in js (Tree Vue.js)

This is my array of objects:
I am using vue.js , I need a tree like this to keep the structure of tree view: https://v2.vuejs.org/v2/examples/tree-view.html
[
{
"name": "",
"children": []
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": []
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2011",
"children": []
}
]
}
]
},
{
"name": "",
"children": [
{
"name": "Titoli",
"children": []
}
]
}
]
I need a function to retrive an object grouped by name with his childrens
{
"name": "",
"children": [
{
"name": "Leggi",
"children": [
{
"name": "2010",
"children": []
},
{
"name": "2011",
"children": []
}
],
"name": "Titoli",
"children": []
}
]
}
I would like to know if there it is a simple way (instead of writing a recursive function), like using lodash or something near it.
Thanks
I think that i have implemented a more readable answer:
const rootTree = [];
const putInTree = (tree, node) => {
let nodeInTree = tree.find(x => x.name === node.name);
if (!nodeInTree) {
nodeInTree = {name: node.name, children: []};
tree.push(nodeInTree);
}
if (node.children[0]) putInTree(nodeInTree.children, node.children[0])
}
nodes.forEach(node => putInTree(rootTree, node));
nodes here is your start array, let me know if this is ok
treeArchive.forEach(element => {
element.children.forEach(father => {
if (result.children.length != 0) {
cicleChildrens(result, father);
function cicleChildrens(padrePrecedente, nuovoPadre){
var brother = padrePrecedente.children.find(x => x.name == nuovoPadre.name);
if (brother != undefined) cicleChildrens(brother, nuovoPadre.children[0]);
else padrePrecedente.children.push(nuovoPadre);
};
}
else result.children.push(father);
});
});
This is currently my working code.. I'm struggling tryng to understand your code #chriss
Try this one:
function getGroupedByName(given) {
let result = given.reduce((a, b) => {
if(!a[b.name]) a[b.name] = [];
a[b.name] = [...a[b.name], ...b.children];
return a;
}, {});
result = Object.keys(result).map(key => ({name: key, children: getByName(result[key])}));
return result;
}
const o = []; // your initial object
getGroupedByName(o, "Leggi")
It is returning it as an array of objects having name and children props, as i am assuming first level can also have multiple different names, not all being ""
It goes first trough all elements in array and groups them into object with structure { name: children } where children is array of all children for same group.
For each children array it preforms same operation, going trough array and flattening it into { name: children } object.
At this moment we have following structure:
{ "": {
Leggi: {...}
}}
When everything is grouped, Object.keys loops trough all keys and breaks it into array where key is name and value children property

Lodash keyBy for multiple nested level arrays

I have an array of object, and i want to convert it into a map of key value pairs with the id as the key. However, I want to do it for both the root level and within the recipes attribute.
Array resp:
[
{
"id": "1",
"recipes": [
{
"id": 4036
},
{
"id": 4041
}
]
},
{
"id": "2",
"recipes": [
{
"id": 4052
},
{
"id": 4053
}
]
}
]
I came across _.keyBy() which maps an attribute as the key, but it doesn't allow nested levels.
Function:
var respObj = _.keyBy(resp, 'id');
Is there an elegant solution to massage resp to make all the objects nested within the array use id as key?
thanks!
you can do it with _.keyBy and _.mapValues
_.chain(resp)
.keyBy('id')
.mapValues(function(item) {
item.recipes = _.keyBy(item.recipes, 'id');
return item;
})
.value();
This is a generic solution that runs _.keyBy recursively on arrays, and the objects inside them:
function deepKeyBy(arr, key) {
return _(arr)
.map(function(o) { // map each object in the array
return _.mapValues(o, function(v) { // map the properties of the object
return _.isArray(v) ? deepKeyBy(v, key) : v; // if the property value is an array, run deepKeyBy() on it
});
})
.keyBy(key); // index the object by the key
}
I've added another level of data in the example (ingredients):
function deepKeyBy(arr, key) {
return _(arr)
.map(function(o) {
return _.mapValues(o, function(v) {
return _.isArray(v) ? deepKeyBy(v, key) : v;
});
})
.keyBy(key);
}
var arr = [{
"id": "1",
"recipes": [{
"id": 4036,
"ingerdients": [{
"id": 5555555
}, {
"id": 5555556
}, {
"id": 5555557
}]
}, {
"id": 4041
}]
}, {
"id": "2",
"recipes": [{
"id": 4052
}, {
"id": 4053
}]
}];
var result = deepKeyBy(arr, 'id');
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
You could get a flattened collection of recipes, concatenate the response and then key by id:
var result = _.chain(resp)
.flatMap('recipes')
.concat(resp)
.keyBy('id')
.value()
The flatMap call will pluck all the recipes from the response and flatten the arrays so we're left with this:
[
{ "id": 4036 },
{ "id": 4041 },
{ "id": 4052 },
{ "id": 4053 }
]
The response is then appended to this array using concat so we then have:
[
{ "id": 4036 },
{ "id": 4041 },
{ "id": 4052 },
{ "id": 4053 },
{ "id": "1", recipes: ... },
{ "id": "2", recipes: ... }
]
Finally we use keyBy to get the required structure .
var resp = [
{
"id": "1",
"recipes": [
{
"id": 4036
},
{
"id": 4041
}
]
},
{
"id": "2",
"recipes": [
{
"id": 4052
},
{
"id": 4053
}
]
}
]
var result = _.chain(resp)
.flatMap('recipes')
.concat(resp)
.keyBy('id')
.value()
document.getElementById('result').textContent = JSON.stringify(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
<p>
<pre id="result"></pre>
</p>

How to search the specific text in a nested object?

For example, I have a JSON array as follow, I will show it on a treelist ,so that user can select the item.Now there is a feature that allowing the user to search the specific text in the list. And the list is a nested list.I hava bind the JSON array to a treelist using HTML and Javascript. Now I should find a good way to search the specific text, and then return the object that include parent node when I find the text.
var allcategories=
[
{
"name": "shoes",
"subcategories": [
{
"name": "man's shoes",
"subcategories": [
{
"name": "sample name"
},
{
"name": "sample name",
"subcategories": []
}
]
},
{
"name": "woman's shoes",
"subcategories": []
}
]
},
{
"name": "cars",
"subcategories": [
{
"name": "cars1",
"subcategories": [
{
"name": "sample name"
},
{
"name": "sample name",
"subcategories": []
}
]
}
]
}
];
So now I want to use the indexOf() function to search the result. If the search is successful, the program should just return the parent category.Anyone have the same question in developing the codes?
As Bergi suggests, you can use a recursive function:
function getCategory(categories, name) {
var category, result;
for (var i=0, iLen=categories.length; i<iLen; i++) {
category = categories[i];
if (category.name == name) {
return category;
} else if (category.subcategories) {
result = getCategory(category.subcategories, name);
if (result) return result;
}
}
// return undefined if category name not found
}
Note that this will return the first subcategory with a matching name, so the name needs to be unique.
#Leeli - you can use this JS lib; DefiantJS (http://defiantjs.com) with which searches in JSON structures becomes a trivial thing. The lib extends the global object JSON with the method "search". With this method, you can search with XPath expressions and it will retusn an array with the matches (empty array if no matches were found). See the example code below.
Here is a working fiddle:
http://jsfiddle.net/hbi99/wXfE6/
var data = [
{
"name": "shoes",
"subcategories": [
{
"name": "man's shoes",
"subcategories": [
{ "name": "heels" },
{ "name": "loafers" }
]
},
{ "name": "woman's shoes" }
]
},
{
"name": "cars",
"subcategories": {
"name": "cars1",
"subcategories": [
{ "name": "Sedan" },
{ "name": "SUV" }
]
}
}
],
res = JSON.search( data, '//*[name="cars1"]/subcategories' );
console.log( res[0].name );
// Sedan
hi here is a jquery function i wrote. since its a associated list in .net the child list is an empty object when there are no child nodes so i have to check is empty instead of simply looking for it to be null. should be backwards compatible though and work as a solution to this question
function findCategory(categories, categoryId)
{
var ret = null;
$.each(categories, function (e, v) {
if (e === categoryId)
ret = v;
else if (!jQuery.isEmptyObject(v.children))
ret = findCategory(v.children, categoryId);
return !ret; //break out of loop when ret not null
});
return ret;
}

Categories