I've looked at lodash documentation and played around with comparing simple objects. I've also found a number of explanations online for comparing entire objects and other types of comparisons, but I want to compare one property value in a single object with the values of all properties of a certain name in a large array with multiple objects.
Is lodash smart enough to do this as is, and, if so, what would be the proper syntax to handle this? Or do I need some sort of loop to work through the larger object and recursively compare its properties of a certain name with the small object property?
The javascript comparison I'm looking for would be something like this, but I don't know how to indicate that I want to compare all itemURL properties in the large array:
// guard clause to end the larger function if test is true, any match found
if (_.isEqual(feedItem.link, rssDataFileArr.itemURL)) {
return;
}
Small object example:
const feedItem = {
link: 'https://news.google.com/rss/search?q=nodejs',
otherProperty: 'whatever'
}
Large array of objects example:
const rssDataFileArr = [
{
"itemURL": "https://news.google.com/rss/search?q=rss-parser",
"irrelevantProperty": "hello"
},
{
"itemURL": "https://news.google.com/rss/search?q=nodejs",
"irrelevantProperty": "world"
},
{
"itemURL": "https://news.google.com/rss/search?q=javascript",
"irrelevantProperty": "hello"
}
]
Any and all help appreciated.
As per suggestion in comment, I went with a built-in javascript method instead of lodash. I used some() because I only needed a true/false boolean result, not a find() value.
const feedItem = {
link: 'https://news.google.com/rss/search?q=nodejs',
otherProperty: 'whatever',
};
const rssDataFileArr = [
{
itemURL: 'https://news.google.com/rss/search?q=rss-parser',
irrelevantProperty: 'hello',
},
{
itemURL: 'https://news.google.com/rss/search?q=nodejs',
irrelevantProperty: 'world',
},
{
itemURL: 'https://news.google.com/rss/search?q=javascript',
irrelevantProperty: 'hello',
},
{
itemURL: 'https://news.google.com/rss/search?q=nodejs',
irrelevantProperty: 'world',
},
];
const linkMatch = rssDataFileArr.some(
({ itemURL }) => itemURL === feedItem.link
);
// guard clause to end the larger function if test is true, any match found
if (linkMatch) {
console.log('linkMatch is true');
return;
}
My Problem:
I'm having a website where I can compare products stored inside an array (with objects). I want to add different filters from array inside of an object that get applied together.
For two filters I can easily do it (see my code below). I just compare two objects and use a filter depending on their content.
But what would be a good approach to use the filter if there are more than two objects. Can I loop through the object and compare if the arrays are empty?
With my current approach I would have to extend my code for every new filter and it would balloon.
What I'm trying to do:
I want to check which filter objects have any data in their "feature" array (that array gets filled after the user clicks a filter on the site) and if they have I want to use these arrays to filter the main filteredArray array.
My current Object:
features_collection: {
aspect_ratio_object: {
features: [],
value: "Aspect Ratio",
},
performance_rating_object: {
features: [],
value: "Performance Rating",
},
},
My Filter Function:
if (
features_collection.aspect_ratio_object.features.length &&
features_collection.performance_rating_object.features.length
) {
return filteredArray.filter(
(obj) =>
features_collection.aspect_ratio_object.features.includes(
obj[features_collection.aspect_ratio_object.value]
) &&
features_collection.performance_rating_object.features.includes(
obj[features_collection.performance_rating_object.value]
)
);
} else if (
features_collection.aspect_ratio_object.features.length ||
features_collection.performance_rating_object.features.length
) {
return filteredArray.filter(
(obj) =>
features_collection.aspect_ratio_object.features.includes(
obj[features_collection.aspect_ratio_object.value]
) ||
features_collection.performance_rating_object.features.includes(
obj[features_collection.performance_rating_object.value]
)
);
}
},
Further Notes:
I can also change my object. I could change it into an array of objects if that would make things easier?
Making your filters an array seems more practical. Here's an example on how to
filter a set of objects against your feature_collection.
function filter_by_features(targets, feature_collection) {
// Start right of to filter the `filteredArray`
return targets.filter(obj => {
// go through every feature and test it against the current object.
// every() returns either true or false and the targets array is filtered
// by that condition supplied within the callback of `every()`
return feature_collection.every(filter => {
// If for a given feature no filter is available, return true
// so the test for this filter passes.
if(filter.features.length === 0) {
return true
}
// there are features, check if any applies.
return filter.features.includes(obj[filter.value])
})
})
}
Usage
// feature collection (as array)
const feature_collection = [
{
features: [],
value: "Aspect Ratio",
},
{
features: [],
value: "Performance Rating",
}
]
// the objects you want to filter.
const objects_to_filter = [/* ... */]
const filtered = filter_by_features(objects_to_filter, feature_collection)
docs
every()
You obviously have too loop through your object.
Here is your loop code for features_collection:
features_collection.forEach(function (item, index) {
console.log(item, index);
});
So I have a payload that pushes firestore collection to an object. Currently when I console.log the object, it shows:
I need it to be in just 1 array, how do I go about that?
I need it to look like (image below) so I can use it with my vuetify data tables
My vuex goes like this:
mutations: {
firebaseOrders(state, payload) {
state.firebaseOrders.unshift(payload)
},
actions: {
getFireBaseOrders(state) {
db.collection("orders").onSnapshot((res) => {
const changes = res.docChanges();
changes.forEach((change) => {
// Push all data to firebaseOrders[]
let payload = change.doc.data();
state.commit("firebaseOrders", payload);
...
This joins the two arrays together.
new_array = old_array[0].concat(old_array[1])
Reference: MDN Web Docs
I need to store data coming from an api in a variable. Only data that contains "true". I have given the API output below.
API Output Data
{
"BNG-JAY-137-003": false,
"BNG-JAY-137-004": true,
"BNG-JAY-137-005": false
}
Below is my function. In this I need to store only data which is true in a variable. Here selected_data is a variable which contains API data.
on(){
for(let key in this.selected_data) {
if(this.selected_data[key]) {
// here I need to store data which is true in an array.
}
}
}
There are several ways to do this. One would be to use Object.keys and filter:
const selected_data = this.selected_data
const array = Object.keys(selected_data).filter(key => selected_data[key])
Closer to your original code would be to just push the keys onto an array:
const selected_data = this.selected_data
const array = []
for (const key in selected_data) {
if (selected_data[key]) {
array.push(key)
}
}
From a Vue perspective this would probably be implemented as a computed property, returning the relevant array at the end. Alternatively it might be stored in a data property, using something equivalent to this.propertyName = array at the end of the method.
You could use a computed property:
computed: {
valid_selected_data: function() {
return Object.keys(this.selected_data).reduce((acc, key) => {
if(this.selected_data[key]) {
acc[key] = this.selected_data[key];
}
return acc;
}, {});
}
}
That code will create another object which holds the same items of your selected_data object, but only the true ones.
If you want just an array with the true keys, then try this:
computed: {
valid_selected_data: function() {
return Object.keys(this.selected_data).filter((key) => this.selected_data[key]);
}
}
I'm learning Redux from this tutorial and I don't get how the spread operator below works in both the object and array. If ...state returns the same thing, how can it work in both situations? I thought it will just return an array, so it will work inside the SHUTTER_VIDEO_SUCCESS because it'll just spread whatever is inside the state into the new array in addition to the action.videos, but how will this work inside the SELECTED_VIDEO case? There is no key to place it in. The spread operator grabs the array not the key value pair from the default initialState right?
initialState.js
export default {
images: [],
videos: []
};
someComponent.js
import initialState from './initialState';
import * as types from 'constants/actionTypes';
export default function ( state = initialState.videos, action ) {
switch (action.type) {
case types.SELECTED_VIDEO:
return { ...state, selectedVideo: action.video }
case types.SHUTTER_VIDEO_SUCCESS:
return [...state, action.videos];
default:
return state;
}
}
UPDATE
Spread syntax allows you to spread an array into an object (arrays are technically objects, as is mostly everything in js). When you spread an array into an object, it will add a key: value pair to the object for each array item, where the key is the index and the value is the value stored at that index in the array. For example:
const arr = [1,2,3,4,5]
const obj = { ...arr } // { 0: 1, 1: 2, 2: 3, 3: 4, 4: 5 }
const arr2 = [{ name: 'x' }, { name: 'y' }]
const obj2 = { ...arr2 } // { 0: { name: 'x' }, 1: { name: 'y' } }
You can also spread strings into arrays and objects as well. For arrays, it will behave similarly as String.prototype.split:
const txt = 'abcdefg'
const arr = [...txt] // ['a','b','c','d','e','f', 'g']
For objects, it will split the string by character and assign keys by index:
const obj = { ...txt } // { 0:'a',1:'b',2:'c',3:'d',4:'e',5:'f',6:'g' }
So you may be getting data that sort of works when you spread an array into an object. However, if the example you gave is what you're actually using, you're going to run into problems. See below.
=============
In the case of reducers in redux, when you use the spread syntax with an array it spreads each item from your array into a new array. It's basically the same as using concat:
const arr = [1,2,3]
const arr2 = [4,5,6]
const arr3 = [...arr, ...arr2] // [1,2,3,4,5,6]
// same as arr.concat(arr2)
With an object, the spread syntax spreads key: value pairs from one object into another:
const obj = { a: 1, b: 2, c: 3 }
const newObj = { ...obj, x: 4, y: 5, z: 6 }
// { a: 1, b: 2, c: 3, x: 4, y: 5, z: 6 }
These are two ways to help keep your data immutable in your reducers. The spread syntax copies array items or object keys/values rather than referencing them. If you do any changes in nested objects or objects in arrays, you'll have to take that into account to make sure you get new copies instead of mutated data.
If you have arrays as object keys then you can spread the entire object into a new one and then override individual keys as needed, including keys that are arrays that need updating with spread syntax. For example, an update to your example code:
const initialState = {
images: [],
videos: [],
selectedVideo: ''
}
// you need all of your initialState here, not just one of the keys
export default function ( state = initialState, action ) {
switch (action.type) {
case types.SELECTED_VIDEO:
// spread all the existing data into your new state, replacing only the selectedVideo key
return {
...state,
selectedVideo: action.video
}
case types.SHUTTER_VIDEO_SUCCESS:
// spread current state into new state, replacing videos with the current state videos and the action videos
return {
...state,
videos: [...state.videos, ...action.videos]
}
default:
return state;
}
}
This shows updating a state object and specific keys of that object that are arrays.
In the example you give, you're changing the structure of your state on the fly. It starts as an array, then sometimes returns an array (when SHUTTER_VIDEO_SUCCESS) and sometimes returns an object (when SELECTED_VIDEO). If you want to have a single reducer function, you would not isolate your initialState to just the videos array. You would need to manage all of your state tree manually as shown above. But your reducer should probably not switch the type of data it's sending back depending on an action. That would be an unpredictable mess.
If you want to break each key into a separate reducer, you would have 3 (images, videos and selectedVideo) and use combineReducers to create your state object.
import { combineReducers } from 'redux'
// import your separate reducer functions
export default combineReucers({
images,
videos,
selectedVideos
})
In that case each reducer will be run whenever you dispatch an action to generate the complete state object. But each reducer will only deal with its specific key, not the whole state object. So you would only need array update logic for keys that are arrays, etc.
According to the tutorial:
create-react-app comes preinstalled with babel-plugin-transform-object-rest-spread that lets you use the spread (…) operator to copy enumerable properties from one object to another in a succinct way. For context, { …state, videos: action.videos } evaluates to Object.assign({}, state, action.videos).
So, that's not a feature of ES6. It uses a plugin to let you use that feature.
Link: https://babeljs.io/docs/plugins/transform-object-rest-spread/
An array is also a key/value-pair but the key is an index. It's using ES6 destructuring and the spread syntax.
Redux docs on the subject
You may also want to read up on ES6 property value shorthand (or whatever it is called):
ES6 Object Literal in Depth
Whenever you find yourself assigning a property value that matches a property name, you can omit the property value, it’s implicit in ES6.