I am trying to find an item in a user's array of items with his id on discord. I have this
const data = inv.findOne({User: message.author.id})
if(!data) {
return message.reply("You own no items. Buy something to trade")
}
if(item === 'hat') {
const itemfound = data.find({User: message.author.id, Items: '🎩Hat'})
if(itemfound) {
return message.reply("FOund!")
}
}
But it keeps returning found when I don't have that item in my array of items
Assuming inv is a Model, inv.findOne(...) returns a Query, and data.findOne(...) also returns a Query. The Query is getting converted to a truthy value in if(itemfound) and falsy in if(!data). Adding await and exec()(optional) statements should fix the issue. Also, data.find().exec() returns an array, which is truthy even if it is empty.
const data = inv.findOne({User: message.author.id})
if(!await data.exec()) {
return message.reply("You own no items. Buy something to trade")
}
if(item === 'hat') {
const itemfound = await data.find({User: message.author.id, Items: '🎩Hat'}).exec()
if(!itemfound.length) {
return message.reply("FOund!")
}
}
Related
I wants to prevent addition of duplicate items to cart. I have tried the code below but it's working only for single item, when there are multiple items in the cart the duplicate items are allowed to add in to the cart. Here is my code
addToCart = (id) => {
let item = this.getItem(id);
if ([...this.state.cart]) {
[...this.state.cart].map((i) => {
if (i.product_name == item.product_name) {
alert("Item is already in cart");
} else {
this.setState((this.state.cart = [...this.state.cart, item]));
}
});
} else {
this.setState((this.state.cart = [...this.state.cart, item]));
}
console.log(this.state.cart);
};
You need to use map only to check if the item already exists, and then either add it or alert that the item is repeated.
One way of doing it would be like this:
existing = [...this.state.cart].map((i) => {
if (i.product_name == item.product_name) {
return i;
}
});
if (existing) {
alert("Item is already in cart");
} else {
this.setState((this.state.cart = [...this.state.cart, item]));
}
Explanation
map function executes the code for each of the items in the collection, which means the moment it finds an item in the cart different from the item selected, it will add the item selected.
So let's say your cart has [apple, orange] and you want to add apple again. When the map code executes it first looks like this:
if ("apple" == "apple") {
alert("Item is already in cart");
} else {
this.setState((this.state.cart = [...this.state.cart, apple]));
}
It doesn't add the item because it already exists... but then it executes a second time, and it looks like this:
if ("orange" == "apple") {
alert("Item is already in cart");
} else {
this.setState((this.state.cart = [...this.state.cart, apple]));
}
It gets added because the second item is different.
What the new code does is that it returns a value only if the item exists and, after looping throuhght all the items in the cart, it checks that value and adds the item if it is nothing.
An item should be added to the cart, if the latter doesn't contain it already.
To check if an Array contains an object, that fulfills a certain condition use the some method, as said by #Isaac Batista.
On the other hand, when you want to update state, by using it's previous value, you should use the callback argument of setState.
See https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
this.setState((state)=>{
// if cart already contains item
if(state.cart.some(itm=>itm.product_name == item.product_name)) {
return {}; // do not update state
} else {
return {cart: state.cart.concat(item)}; // add item to cart
}
}
You can use filter method to check whether the item is already available or not. With this you can also avoid the nested if condition also.
addToCart = (id) => {
let item = this.getItem(id);
let checkCart = [...this.state.cart].filter((i) => {
return i.product_name == item.product_name;
});
if (checkCart.length !== 0) {
alert("Item is already in cart");
} else {
this.setState((this.state.cart = [...this.state.cart, item]));
}
console.log(this.state.cart);
};
Here is a functional exemple, but note some points:
You are mutating state, and you should not do it, as it is explained here. So, you should just call setState passing the new value via argument, like this: this.setState(newValue).
A map is used to create a new array, the correct function to find out if some element passes a rule is some. This will allow you to check if some product inside cart is the clicked product.
// quick example
[1,2,3,4].some(number => number === 2) // true
[1,2,3,4].some(number => number === 5) // false
Finally, i would do something like this
const { cart } = this.state;
const product = this.getItem(id);
// returns true if there is any product with the same id
const isProductInCart = cart.some((item) => item.id === product.id);
if (isProductInCart) {
alert("Product already in cart");
} else {
this.setState({
cart: [...cart, product]
});
}
I'm currently completing a project where I have to build a Spotify playlist creator. For part of this, I need to add or remove tracks from the playlist. I've coded a method that appears to work, but it is different from the official solution, so I just want to see whether there is a reason I shouldn't do it my way.
Specifically, they use .find and .filter methods where I have used .includes. Is there a downside to what I've done?
Their code
addTrack(track) {
let tracks = this.state.playlistTracks;
if (tracks.find(savedTrack => savedTrack.id === track.id)) {
return;
}
tracks.push(track);
this.setState({ playlistTracks: tracks});
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
tracks = tracks.filter(currentTrack => currentTrack.id !== track.id);
this.setState({playlistTracks: tracks});
}
My code
addTrack(track) {
let tracks = this.state.playlistTracks;
if (!tracks.includes(track)) {
tracks.push(track);
}
this.setState({playlistTracks: tracks});
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
if (tracks.includes(track)) {
let index = tracks.indexOf(track);
tracks.splice(index, 1);
}
this.setState({playlistTracks: tracks});
}
Yes, there is a significant difference, because includes() will only return true if you pass it the actual instance (by that I mean a reference that points to the same object) of track that you are looking for.
The provided solution compares tracks only based on the track ID, so that is something different.
See the following example:
const tracks = [
{id: 1, title: "mysong1"},
{id: 2, title: "mysong2"},
]
function isTrackPresentIncludes(track){
return tracks.includes(track);
}
function isTrackPresentFind(track){
return tracks.find(it => it.id === track.id) !== undefined;
}
// this will be true
console.log("with includes(tracks[0]):\n", isTrackPresentIncludes(tracks[0]))
// this will be false as it's a different object
console.log("with includes({id: 1, title: \"mysong1\"}):\n", isTrackPresentIncludes({id: 1, title: "mysong1"}))
// this will be true
console.log("with find(tracks[0]):\n", isTrackPresentFind(tracks[0]))
// this will also be true
console.log("with find({id: 1, title: \"mysong1\"}):\n", isTrackPresentFind({id: 1, title: "mysong1"}))
You have the same issue with indexOf() in your removeTrack().
There is another thing I don't particularly like about the solution. find() returns the track that was found but that return value is never actually used so to my mind you should use some() instead which just returns true or false.
I don't think this is a problem here but it could potentially lead to unexpected behavior if an array would hold falsy values.
Consider this:
const arrayWithFalsyValues = [
0, // zero is falsy!
1,
2,
3,
4,
5,
6,
7,
8
]
function isPresent(toBeFound){
if(arrayWithFalsyValues.find(number => number === toBeFound)){
console.log(`Value ${toBeFound} found in array`);
}
else{
console.log(`Value ${toBeFound} NOT found in array`);
}
}
console.log("Array:", arrayWithFalsyValues)
// this will work as expected
console.log("Contains 3?")
isPresent(3)
console.log("Contains 8?")
isPresent(8)
console.log("Contains 10?")
isPresent(10)
// now search for the falsy value -> incorrect result
console.log("Contains 0?")
isPresent(0)
Issue is with referencing, You have make another reference of playlistTracks
addTrack(track) {
let { playlistTracks } = this.state;
let tracks = [...playlistTracks];
if (!tracks.includes(track)) {
tracks.push(track);
}
this.setState({ playlistTracks: tracks });
}
removeTrack(track) {
let { playlistTracks } = this.state;
let tracks = [...playlistTracks];
if (tracks.includes(track)) {
let index = tracks.indexOf(track);
tracks.splice(index, 1);
}
this.setState({ playlistTracks: tracks });
}
MY SUGGESTION
addTrack(track) {
const { playlistTracks } = this.state;
const tracks = [...playlistTracks];
const index = tracks.indexOf(track);
if (index < 0) {
tracks.push(track);
}
this.setState({ playlistTracks: tracks });
}
removeTrack(track) {
const { playlistTracks } = this.state;
const tracks = [...playlistTracks];
const index = tracks.indexOf(track);
if (index > -1) {
tracks.splice(index, 1);
}
this.setState({ playlistTracks: tracks });
}
I am trying to figure out how to do this but can't seem to wrap my head around it..
I have an address object
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
I want to check for any key that has a value but only specific keys
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
I want to check all keys in objProps against my address object and if any one of them contains a value return true (doesn't matter if its 1 or all 6).. If all keys don't contain a value then return false (Sometimes I will get an address object that has all null values)
I've tried various ways to accomplish this but have failed in each one.
The variation I am working on now is using reduce. While it doesn't meet my needs I thought I could check the resulting array and if length was greater than 0 than I have my answer..
Work-in-progress:
function hasAddressData(obj: any) {
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const keysWithData = objProps.reduce((accumulator, key) => {
const propExistsOnObj = obj.hasOwnProperty(key);
let keyHasData = [];
if (obj[key].length > 0 ) {
keyHasData = obj[key]
}
if (!propExistsOnObj) {
accumulator.push(key);
} else if (keyHasData) {
const equalValueKeyIndex = accumulator.indexOf(key);
accumulator.splice(equalValueKeyIndex, 1);
}
return accumulator;
});
return keysWithData;
}
The above is messed up I know and doesn't work.. Just learning this stuff.. anyone have a suggestion or comment?
Check that .some of the objProps, when looked up on the obj, contain a value. (Either with Boolean or by comparing against '')
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"",
"addressLine2":"",
"city":"",
"state":"Indiana",
"state_code":"",
"zip":"",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":""
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
function checkKeys(target, props) {
return props.some((prop) => {
return target.hasOwnProperty(prop) && target[prop];
});
}
Explanation: some iterates through the props you want to check, returning true immediately when one is found (i.e. the callback returns true). If no props are found (i.e. no callback returns true), some returns false.
hasOwnProperty ensures that you are only checking properties on target, and not looking up the prototype chain. target[prop] checks for a truthy value. You may need to modify this last check if you're going to be handling values other than strings.
I am doing file selection and push the data into an array but if the selected data has already exist in the array I want to remove it.
I am pushing my data :
_setSelectedFile(file_uri, file_key){
let selectedFiles = [...this.state.selectedFiles];
selectedFiles.push({ file_uri: file_uri, file_key: file_key });
this.setState({ selectedFiles });
}
The output of my array is something like this :
[
{
file_uri: "ph://9F983DBA-EC35-42B8-8773-B597CF782EDD/L0/001",
file_key: "2"
},
{
file_uri: "ph://CC95F08C-88C3-4012-9D6D-64A413D254B3/L0/001",
file_key: "5"
}
]
I stored the file_key as a reference when removing it later. I saw this answer Delete item from state array in react but not sure how to apply it since the question from the discussion is referring to one-dimensional array.
I tried out some trick and apparently it's working in my case. Hope this helps others too.
_setSelectedFile(file_uri, file_key){
var isExist = false;
var selectedFileKey = null;
let selectedFiles = [...this.state.selectedFiles];
if(this.state.selectedFiles != null){
this.state.selectedFiles.map((data, i)=>{
if(data.file_key === file_key){
isExist = true;
selectedFileKey = i;
}
})
}
if(isExist == true){
selectedFiles.splice(selectedFileKey, 1);
this.setState({selectedFiles: selectedFiles});
} else {
selectedFiles.push({ file_uri: file_uri, file_key: file_key });
this.setState({ selectedFiles });
}
}
So I do mapping and check if the data is already exist then assign isExist = true and store the key value selectedFileKey = i.
With isExist set as true I can proceed with removing the data from my array.
I'm trying to filter a JSON array using another JSON array criteria that I have using (filter).
Here is my code:
function filterArray(object, criteria){
return object.filter(function(obj){
for(var i=0;i<criteria.length;i++){
let criteriaEle = criteria[i];
return Object.keys(criteriaEle).forEach(function(key){
if(obj[key] == criteriaEle[key]){
return obj;
}
})
}
})
}
For example:
object = [{type:1,company:1,color:0,name:a},{type:2,company:1,color:0,name:b},{type:1,company:3,color:0,name:c},{type:4,company:1,color:0,name:d},{type:1,company:1,color:1,name:e}]
criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}]
So if I give these two arrays to the function it should return
obj = [{{type:1,company:1,color:0,name:a},{type:1,company:1,color:1,name:e}}]
I'm not sure where am I going wrong in this. Please help.
Update:
Also, I do not want to use obj.type or obj.company or object.color as parameters to search as I want to make my code maintainable and do not want to come and update it later if in future more criteria's are added.
const data = [{type:1,company:1,color:0,name:'a'},{type:2,company:1,color:0,name:'b'},{type:1,company:3,color:0,name:'c'},{type:4,company:1,color:0,name:'d'},{type:1,company:1,color:1,name:'e'}];
const criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}];
function checkCriteria(obj) {
return criteria.some(criterion => {
for (const key in criterion) {
if (criterion[key] !== obj[key]) {
return false;
}
}
return true;
});
}
const filtered = data.filter(checkCriteria);
console.log('Filtered array: ', filtered);
Here is one solution.
Here are some references
Array.some
Array.filter
Based on the comment, adding another snippet to explain the concept of closures.
const data = [{type:1,company:1,color:0,name:'a'},{type:2,company:1,color:0,name:'b'},{type:1,company:3,color:0,name:'c'},{type:4,company:1,color:0,name:'d'},{type:1,company:1,color:1,name:'e'}];
function createCriteriaValidationFunction(criteria) {
return function checkCriteria(obj) {
return criteria.some(criterion => {
for (const key in criterion) {
if (criterion[key] !== obj[key]) {
return false;
}
}
return true;
});
}
}
const criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}];
const filtered = data.filter(createCriteriaValidationFunction(criteria));
console.log('Filtered array: ', filtered);
It's the same concept as before, however, criteria was defined in the file. This time, criteria can be defined outside and can be passed in to the function. The trick is to create the checkCriteria function on the fly with criteria passed in and available in the closure. In both cases, criteria variable is available in the scope in which checkCriteria is executed.