How to find many results from an array with Lodash? - javascript

The following code loops thorugh an Object array, uses _.find to find the object of a certain category and push it into the panoramaCats array:
this.panoramas.forEach(panorama => {
const panoramaCat = _.find(this.panoramas, { category: panorama.category })
const panoramaCats = []
panoramaCats.push(panoramaCat)
payload[panorama.category] = panoramaCats
})
I thought _.find would find ALL the objects with that category, but it only finds the first one.
How to change the code so _.find finds ALL the object with that category?

Use ._matches to find objects matching your criteria.
this.panoramas.forEach(panorama => {
const panoramaCats = _.find(this.panoramas, _.matches({ category: panorama.category }))
payload[panorama.category] = panoramaCats
})

this.panoramas.forEach(panorama => {
payload[panorama.category] = _.find(this.panoramas, {category: panorama.category})
})

Have you considered using _.groupBy? I think it's simpler than manually looping through your collection.
payload = _.groupBy(this.panoramas, 'category')
If you need to preserve other pre-existing properties in your payload object you could use _.merge:
payload = _.merge(payload, _.groupBy(this.panoramas, 'category'))

Related

How do I search array within an object for any instances of specified string values?

How do I search an array for any instances of multiple specified string values?
const arrayOfObjects = [{
name: box1,
storage: ['car', 'goat', 'tea']
},
{
name: box2,
storage: ['camel', 'fox', 'tea']
}
];
arrayOfSearchItems = ['goat', 'car', 'oranges'];
If any one or all of the arrayOfSearchItems is present in one of the objects in my array, I want it to either return false or some other way that I can use to excluded that object that is in my arrayOfObjects from a new, filtered arrayOfObjects without any objects that contained the arrayOfSearchItems string values. In this case I would want an array of objects without box1.
Here is what I have tried to do, based on other suggestions. I spent a long time on this. The problem with this function is that it only works on the first arrayOfSearchItems strings, to exclude that object. It will ignore the second or third strings, and not exclude the object, even if it contains those strings. For example, it will exclude an object with 'goat'. Once that happens though, it will no longer exclude based on 'car'. I have tried to adapt my longer code for the purposes of this question, I may have some typos.
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => {
let incrementArray = [];
let userEffects = arrayOfSearchItems;
let objects = arrayOfObjects;
for (i = 0; i < userEffects.length; i++) {
for (x = 0; x < objects.length; x++) {
if (objects[x].storage.indexOf(userEffects) <= -1) {
incrementArray.push(objects[x]);
}
}
}
return(incrementArray);
}
let filteredArray = excludeItems(arrayOfSearchItems, arrayOfObjects);
console.log(filteredArray);
Thanks for providing some example code. That helps.
Let's start with your function, which has a good signature:
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => { ... }
If we describe what this function should do, we would say "it returns a new array of objects which do not contain any of the search items." This gives us a clue about how we should write our code.
Since we will be returning a filtered array of objects, we can start by using the filter method:
return arrayOfObjects.filter(obj => ...)
For each object, we want to make sure that its storage does not contain any of the search items. Another way to word this is "every item in the starage array does NOT appear in the list of search items". Now let's write that code using the every method:
.filter(obj => {
// ensure "every" storage item matches a condition
return obj.storage.every(storageItem => {
// the "condition" is that it is NOT in the array search items
return arrayOfSearchItems.includes(storageItem) === false);
});
});
Putting it all together:
const excludeItems = (arrayOfSearchItems, arrayOfObjects) => {
return arrayOfObjects.filter(obj => {
return obj.storage.every(storageItem => {
return arrayOfSearchItems.includes(storageItem) === false;
});
});
}
Here's a fiddle: https://jsfiddle.net/3p95xzwe/
You can achieve your goal by using some of the built-in Array prototype functions, like filter, some and includes.
const excludeItems = (search, objs) =>
objs.filter(({storage:o}) => !search.some(s => o.includes(s)));
In other words: Filter my array objs, on the property storage to keep only those that they dont include any of the strings in search.

Filtering an object array with another array

I am having a filtering problem..
objArray is the array that needs to be filtered.
selectedNames is an array that contains the values that I want to find in objArray.
I need to fetch all objects that have one or more values from selectedNames in their "names" property (an array) .
The output I am trying to get is :
let result = [{names:["A","B","C","D"]},
{names:["A","B"]},
{names:["A","D"]}
]
Here is a simplified version of my code:
let objArray = [{names:["A","B","C","D"]},
{names:["C","D"]},
{names:["C","D","E"]},
{names:["A","B"]},
{names:["A","D"]}
]
let selectedNames = ["A","B"]
result = this.objArray .filter(obj => {
return this.selectedNames.includes(obj.names)
}
My code seems to work fine if names attribute was a single value and not an array. But I can't figure out how to make it work on an array.
Any help is more than welcome
You could do something like this. Filtering the array based on the names property having 'some' value be included in the selectedNames array.
...
objArray.filter(obj => obj.names.some(name => selectedNames.includes(name)));
[Edit] As #RadicalTurnip pointed out, this is not performant if the selectedNames is too large. I would suggest that you use an object. E.x
...
const selectedNamesMap = selectedNames.reduce((p,c) => ({...p, [c]: true}), {});
objArray.filter(obj => obj.names.some(name => selelectedNamesMap[name]));
Overkill, but if the arrays are really large (millions of elements) then you are better of using regular for loop and not array methods.
This result is not performant scaled up, but I don't know that there is any way to ensure that it will be performant unless you know more information (like the names are already sorted). That being said, you just missed one more piece of logic.
result = this.objArray.filter(obj => {
let toReturn = false;
obj.names.forEach(name => {
if (this.selectedNames.includes(name))
toReturn = true;
};
};
return toReturn;
};

How to compare if two arrays have duplicate values and return the different value?

I have two arrays and I would like to compare if these arrays have duplicated values, then return the values that aren't duplicates. Based on these two arrays I would like to return the string Eucalipto.
const plants = [
{
id: 59,
kind: "Cana-de-açucar"
},
{
id: 60,
kind: "Citros"
}
];
const auxPlants = [
"Cana-de-açucar",
"Citros",
"Eucalipto"
]
You can use Array#map to find all the kind values, pass that to the Set constructor, and then use Array#filter to find all elements of the array not in that Set.
const plants = [
{
id: 59,
kind: "Cana-de-açucar"
},
{
id: 60,
kind: "Citros"
}
];
const auxPlants = [
"Cana-de-açucar",
"Citros",
"Eucalipto"
];
const set = new Set(plants.map(({kind})=>kind));
const res = auxPlants.filter(x => !set.has(x));
console.log(res);
sounds like you want to filter the array of values you're interested in based on if they're not found in the other array, like so:
const nonDuplicates = auxPlants.filter(a => !plants.find(p => p.kind === a))
it's unclear if you'd also want values from the plants array that are non duplicate as well, or if you're only interested in uniques from the auxPlants array
This is the solution to it, I have explained it's working using comments
// create a set in order to store values in it
// assuming you have unique values
let set = new Set();
// iterating over array of object and storing the value of 'kind' in the set
for(obj of plants){
set.add(obj.kind);
}
// iterating over array and checking for values in set,
// if not in set then printing it
for(ele of auxPlants){
if(!set.has(ele)){
console.log(ele);
}
}
As said, please search for an already posted solution first. Here's what I found.
Anyhow, the solution would be to separate the types of plants from the first array, as so:
const plantsTypes = plants.map(obj => obj.kind)
Then, filter out the non duplicates:
const nonDuplicates = auxPlants.filter(plant => !plantsTypes.includes(plant))
Note that it matters which array you call the .filter() function on.

Push unique objects into array in JAVASCRIPT

I want to push object that only have unique id1 into array.
Example:
let array = [],
obj = {},
access = true
if(access){
obj['id1'] = 1
obj['id2'] = 2
if(array.indexOf(obj.id1) == -1){
array.push(obj)
}
}
console.log(array);
In the above example I am trying to add value to obj then push the obj into array. But obj.id1 need to be unique. The method I am using above doesn't work in my case.
Thank you
As Taplar says, indexOf will look for the first instance of the thing you pass in in the other array. This won't work because there are no instances of the ids in the array, directly anyway.
Use the find function, which allow you pass in a comparison function, so you can define what a match is.
let initial = [{id:1}, {id:2}, {id:1}];
let result = initial.reduce((acc, item) =>
{
if(!acc.find(other => item.id == other.id))
{
acc.push(item);
}
return acc;
}, []);
console.log(result);
Simplest solution . Lets say myObjArray have duplicate object use below es6 code to get unique array from that
// Creates an array of objects with unique "name" property values.
let uniqueObjArray = [
...new Map(myObjArray.map((item) => [item["name"], item])).values(), ]; console.log("uniqueObjArray", uniqueObjArray);
Refer here for more detail https://yagisanatode.com/2021/07/03/get-a-unique-list-of-objects-in-an-array-of-object-in-javascript/
I think you need to use findIndex and not indexOf.
Try replacing your if condition with the following:
array.findIndex((o)=>{ return o.id1 === obj.id1 }) === -1

lodash filter data by multiple filters

I have this data and need to apply all the filters using lodash. The final output is blank.
filters = [{'gender':'M'}, {'division':'police'}]
data = [{'name':'tom', 'gender':'M', 'division':'police'},
{'name':'bob' , 'gender':'M', 'division':'police'},
{'name':'dave' , 'gender':'F', 'division':'IT'},
]
data = _.filter(data, function(item){
_.forEach(filters, function(filter){
data_found = _.filter([item], filter)
console.log('data_found - ', data_found)
})
})
console.log(data)
https://jsfiddle.net/52k6170c/1/
You can combine your filters array to a single object and use that as your filter
combined = filters.reduce((acc,item) => ({...acc, ...item}))
// combined is = {'gender':'M', 'division':'police'}
data = _.filter(data, combined)
if you feel like not using the rest operators, there is a lodash function that combines the array of objects into a single object
You can just combine both filter into one. Something like this
_.filter(data, function(item){
if(item.gender === 'M' && item.division === 'police') return item;
})
This would give you all records with gender-male and division-police
You can use the _.filter method along with the _.every method to filter a collection based on another collection of predicates (I'm using arrow functions here). This example also assumes your filter objects only have a single key/value pair as in your example:
const filteredData = _.filter(data, item => {
return _.every(filters, filter => {
let filterKey = Object.keys(filter)[0]
return item[filterKey] === filter[filterKey]
})
})
A cleaner solution would be to combine your filters into a single object, and use the _.matches method to do a comparison:
const filter = { gender: 'M', division: 'police' }
const filteredData = _.filter(data, _.matches(filter))

Categories