How to loop through an array containing objects and do comparison - ionic4

I am using ionic 4. I get the result from the API then get the result show like this
[
{"name":John,"age":20},
{"name":Peter,"age":35},
{"name":Alex,"age":15}
]
But I want to get the name only to check whether have same name with my condition or not. But I cannot straight a way get the result from the API, I need to hard code to do comparison. Here is my code:
this.http.get(SERVER_URL).subscribe((res) => {
const data = [
{ name: John, age: 21 },
{ name: Thomas, age: 25 },
];
const ppl= data.find(people=> people.name === 'alex');
console.log(ppl);
});
So, My first question is How to get the name from the API directly, not like now I hard code the result from API. My Second Question is when I do comparison I want to show the result 'already exist' or 'can use this name'. Because if I write my code like this I will get the error Type 'void' is not assignable to type 'boolean':
const ppl= data.find((people)=> {
if(people.name === 'alex') {
this.text = 'already exist'
} else {
this.text = 'can use this name'
}});
console.log(ppl);
Anyone can help me? Thank you very much

Instead of defining data, use the contents of the response; res will have the exact same contents that you are declaring in data.
this.http.get(SERVER_URL).subscribe(res => {
// If successful, res is an array with user data like the following
// [
// {name: "John", age: 21},
// {name: "Thomas", age: 25},
// ...
// ]
if (res.find(user => user.name === 'alex')) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}
});
Taken from the MDN docs on Array.prototype.find():
The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
In that case
res.find(user => user.name === 'alex')
will return a user object if any of the usernames match alex, or undefined if none of the user.name attributes match alex.
undefined evaluates to false and a user object evaluates to true in the conditional.
Keep in mind that you are comparing strings with ===, so, for example, Alex will not match alex, if you want to look into other ways to compare strings, have a look at this question.
You also might want to handle errors, how you handle them is up to you, and it will depend on the response, but you can access the error inside your subscribe like this:
this.http.get(SERVER_URL).subscribe(res => {
if (res.find(user => user.name === 'alex')) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}
}, error => {
console.log(error);
}, () => {
// There is also a 'complete' handler that triggers in both cases
});
Edit. API returns Object not array
If your API returns an Object instead of an array like in your question, you can still iterate over the properties
this.http.get(SERVER_URL).subscribe(res => {
// If successful, res is an array with user data like the following
// {
// key1: {name: "John", age: 21},
// key2: {name: "Thomas", age: 25},
// ...
// }
let match = false;
Object.keys(res).forEach(key => {
if (res[key].name === 'alex') {
match = true;
}
});
if (match) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}
});
Instead of Object.keys() you could use Object.values() to get an array with user objects, then use find() as before, but that seems less efficient, something like this:
if (Object.values(res).find(user => user.name === 'alex')) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}

Related

Vue: Add items to computed property results which do not exist my Array of Objects

I have a computed property that filters the results on the date:
resultfilteredResults() {
const filteredResults = this.results.filter((result) => {
return Date.now() < new Date(result.metaData.E);
});
return filteredResults;
},
That works fine.
Now I have realized that my filteredResults need to contain data that does not necessarily exist in the specific Object.
For example. One bit of data within the object in the Array looks like this:
"C": "Pakistan, Vietnam, Wales, Western Sahara, Yemen, Zambia"
Sometimes "C" will not exist (when this is the case it means it should bring back all available data in all "C" objects within the whole Array. This is because it is not only for specific counties but all countries. I hope that makes sense.
I tried this but it does not work.
resultfilteredResults() {
const undefinedResults = result.metaData;
{
const filteredResults = this.results.filter((result) => {
return Date.now() < new Date(result.metaData.E);
});
if (undefinedResults == "undefined") {
return undefinedResults;
} else {
return filteredResults;
}
}
},
Can anyone help?
There is nothing wrong with the code.
What is not clear is if (undefinedResults == "undefined") {
undefinedResult is a string "undefined"? or just undefined?
Can you try this?
if (undefinedResults == undefined) {

VUEJS Can’t use api response data in the template

I need to populate a table using an array of objects got by an api call (axios).
This part is working fine.
In the store module (activity.js) I declared the array:
currentUserActivities: [],
In the mutations:
SET_CURRENT_USER_ACTIVITIES: (state, currentUserActivities) => {
state.currentUserActivities = currentUserActivities
},
In the actions:
setCurrentUserActivities({ commit }, userId) {
return new Promise((resolve, reject) => {
getUserActivities(userId).then(response => {
const currentUserActivities = response.results
commit('SET_CURRENT_USER_ACTIVITIES', currentUserActivities)
console.log('response current user activities: ', response.results)
resolve()
}).catch(error => {
console.log('Error setting single user activities: ', error)
reject(error)
})
})
},
Then I saved it in the getters module as so:
currentUserActivities: state => state.activity.currentUserActivities,
In the vue page, the relevant part of the script:
data() {
return {
currentUser: {},
userId: {
type: Number,
default: function() {
return {}
}
},
currentUserActivities: [],
}
},
mounted() {
const userId = this.$route.params.userId
this.$store.dispatch('user/setCurrentProfile', userId).then(() => {
const currentUser = this.$store.getters.currentProfile.user
this.currentUser = currentUser
console.log('user mounted user', currentUser)
this.$store.dispatch('activity/setCurrentUserActivities', userId).then(() => {
const currentUserActivities = this.$store.getters.currentUserActivities
console.log('activities on mounted', currentUserActivities)
})
})
},
In the template part, as I said, I will have a table data. Let's forget about it for now, I am just trying to get the array displayed raw, as so:
<div>
<p v-if="currentUserActivities.length = 0">
This user has no activities yet.
</p>
<p>CURRENT ACTIVITIES: {{ currentUserActivities }}</p>
<p>CURRENT USER: {{ currentUser }}</p>
</div>
The current user is displaying fine, in the browser I can see:
CURRENT USER: { "id": 1, "last_login": "20/09/2019 09:42:15", "is_superuser": false, "username": "admin", "first_name": "System", "last_name": "Dev", "email": "systems#dev.it", "is_staff": true, "is_active": false, "date_joined": "30/08/2019 09:03:40" }
The current user activities array, instead:
CURRENT ACTIVITIES: []
In the console I have both, leaving the user which is fine, the current user activities array is:
activities on mounted:
0: {...}
1: {…}
2:
activity: (...)
arrival_point: "SRID=4326;POINT (0 0)"
burns_calories: false
co2: "0.00"
co2_production: (...)
cost: (...)
created: (...)
default_cost: (...)
end: (...)
ecc. It's there, we can see it.
Inside the mounted, if we compare the code written for the user and the activities, the only difference is that I didn't set
this.currentUserActivities = currentUserActivities
If I do that, I loose the data in the console too (on the screen it remains empty array).
In the console I would have:
activities on mounted: (5) [{…}, {…}, {…}, {…}, {…}, __ob__: Observer]
1. length: 0
2. __ob__: Observer {value: Array(0), dep: Dep, vmCount: 0}
3. __proto__: Array
Also, even if I set
v-if="currentUserActivities.length = 0"
to display a p tag in case the array is really empty, it doesn't get displayed. This too is not right. I don't know if they can be related.
I tried many many subtle different versions of code, but none of them worked.
I know I am missing something (code is never wrong....) ....
Can someone enlighten me, please?
Thanks a lot.
x
First up, this:
this.$store.dispatch('activity/setCurrentUserActivities', userId).then(() => {
const currentUserActivities = this.$store.getters.currentUserActivities
console.log('activities on mounted', currentUserActivities)
})
As you've noted in the question, you aren't assigning currentUserActivities to anything. It should be this:
this.$store.dispatch('activity/setCurrentUserActivities', userId).then(() => {
const currentUserActivities = this.$store.getters.currentUserActivities
this.currentUserActivities = currentUserActivities
console.log('activities on mounted', currentUserActivities)
})
I know you mentioned that this didn't work in the question but it is required to get it working. It isn't sufficient, but it is necessary.
The reason the array appears empty is because of this:
v-if="currentUserActivities.length = 0"
Note that you are setting the length to 0, not comparing it to 0. It should be:
v-if="currentUserActivities.length === 0"
You've got some other problems too, though they're not directly related to the empty array.
Generally you shouldn't have data values for state in the store (unless you're taking copies for editing purposes, which you don't seem to be). Instead they should be exposed as computed properties, e.g.:
computed: {
currentUser () {
return this.$store.getters.currentProfile.user
}
}
Vuex includes a helper called mapGetters that can be used to shorten this a little, see https://vuex.vuejs.org/api/#component-binding-helpers, though some people prefer the explicitness of the longer form.
This is also a little strange:
return new Promise((resolve, reject) => {
getUserActivities(userId).then(response => {
Generally creating a new promise is regarded as a code smell as it is very rarely necessary. In this case you should probably just be returning the promise returned by getUserActivities instead. e.g.:
return getUserActivities(userId).then(response => {
Obviously you'd need to make other adjustments to accommodate the resolve and reject functions no longer being available. Instead of resolve you'd just return the relevant value (though there doesn't seem to be one in your case) and for reject you'd just throw the error instead.
I also notice that userId in your data is being assigned a type and default. Note that this is prop syntax and isn't valid for data properties. It isn't an error but the userId will just be equal to that whole object, it won't treat it as a configuration object.

Using map to reduce in Gun

I am new to Gun. I have existing code that very effectively reduces an array of objects based on a pattern. I am thinking I should tweak this to run in the context of Gun's .map and return undefined for non-matches. I think I will also have to provide two arguments, one of which is the where clause and the other the properties I want shown on returned objects. I also presume that if I use .on future matches will automagically get spit out! Am I on the right path?
const match = (object,key,value) => {
const type = typeof(value);
if(value && type==="object") {
return Object.keys(value).every(childkey =>
match(object[key],childkey,value[childkey]));
if(type==="function") return value(object[key]);
return object[key]===value;
}
const reduce = (objects,where) => {
const keys = Object.keys(where);
return objects.reduce((accumulator,current) => {
if(keys.every(key => match(current,key,where[key]))) {
accumulator.push(current);
}
return accumulator;
},[]);
}
let rows = reduce([{name: "Joe",address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16},
{name: "Joe",address:{city: "New York"},age:20}],
{name: () => true,
address: {city: "Seattle"},
age: (age) => age > 10});
// results in
[{name: "Joe",address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16}]
Further exploration of this resulted in the code below, which is stylistically different, but conforms to the immediate responsive nature of Gun. However, it is unclear how to deal with nested objects. The code below only works for primitives.
const match = (object,key,value) => {
const type = typeof(value);
if(!object || typeof(object)!=="object") return false;
if(value && type==="object") {
const child = gun.get(object[key]["#"]);
for(let key in value) {
const value = {};
child.get(key).val(v => value[key] = v,{wait:0});
if(!match(value,key,value[key])) return;
}
}
if(type==="function") return value(object[key]);
return object[key]===value;
}
const gun = Gun(["http://localhost:8080/gun"]),
users = [{name: "Joe",address:{city: "Seattle"},age:25},
{address:{city: "Seattle"},age:25},
{name: "Mary",address:{city: "Seattle"},age:16},
{name: "Joe",address:{city: "New York"},age:20}];
//gun.get("users").map().put(null);
for(let user of users) {
const object = gun.get(user.name).put(user);
gun.get("users").set(object);
}
gun.get("users").map(user => {
const pattern = {name: (value) => value!=null, age: (age) => age > 20}; //, address: {city: "Seattle"}
for(let key in pattern) {
if(!match(user,key,pattern[key])) return;
}
return user;
}).on(data => console.log(data));
Yes. GUN's .map method does more than what it seems.
Say we have var users = gun.get('users'). We can do:
users.map() with no callback acts like a forEach because the default callback is to return the data as-is.
users.map(user => user.age * 2) with a callback, it lets you transform the data like you would expect from a map, except where:
users.map(function(){ return }) if you return undefined, it will filter out that record.
WARNING: As of the current time, .map(transform) function is currently experimental and my have bugs with it. Please try it and report any you find.
Now we can combine it with some other methods, to get some cool behavior:
users.map().on(cb) will get current and future users as they are added to the table, and gets notified for updates on each of those users.
users.map().val(cb) will get current and future users as they are added to the table, but only gets each one once.
users.val().map().on(cb) gets only the current users (not future), but gets the updates to those users.
users.val().map().val(cb) gets only the current users (not future), and only gets them once.
So yes, you are on the right track. For instance, I have a test in gun core that does this:
list.map(user => user.age === 27? user.name + "thezombie" : u).on(function(data){
// verify
});
list.set({name: 'alice', age: 27});
list.set({name: 'bob', age: 27});
list.set({name: 'carl', age: 29});
list.set({name: 'dave', age: 25});
This creates a live map that filters the results and locally (view only) transforms the data.
In the future, this is how the SQL and MongoDB Mango query extensions will work for gun.
Note: GUN only loads the property you request on an object/node, so it is bandwidth efficient. If we do users.map().get('age') it will only load the age value on every user, nothing else.
So internally, you can do some efficient checks, and if all your conditionals match, only /then/ load the entire object. Additionally, there are two other options: (1) you can use an in-memory version of gun to create server-side request-response patterns, so you can have server-side filtering/querying that is efficient. (2) if you become an adapter developer and learn the simple wire spec and then write your own custom query language extensions!
Anything else? Hit me up! More than happy to answer.
Edit: My reply in the comments, comments apparently can't have code. Here is pseudo-code of how to "build up" more complex queries, which will be similar to how SQL/Mango query extensions will work:
mutli-value & nested value matching can be "built up" from this as the base, but yes, you are right, until we have SQL/Mango query examples, there isn't a simple/immediate "out of the box" example. This is pseudo code, but should get the idea across:
```
Gun.chain.match = function(query, cb){
var gun = this;
var fields = Object.keys(query);
var check = {};
fields.forEach(function(field){
check[field] = true;
gun.get(field).val(function(val){
if(val !== query[field]){ return }
check[field] = false;
//all checks done?
cb(results)
});
});
return gun;
}
```
Solution, the trick is to use map and not val:
Gun.chain.match = function(pattern,cb) {
let node = this,
passed = true,
keys = Object.keys(pattern);
keys.every(key => {
const test = pattern[key],
type = typeof(test);
if(test && type==="object") {
node.get(key).match(test);
} else if(type==="function") {
node.get(key).map(value => {
if(test(value[key])) {
return value;
} else {
passed = false;
}
});
} else {
node.get(key).map(value => {
if(value[key]===test) {
return value;
} else {
passed = false;
}
});
}
return passed;
});
if(passed && cb) this.val(value => cb(value))
return this;
}
const gun = new Gun();
gun.get("Joe").put({name:"Joe",address:{city:"Seattle"},age:20});
gun.get("Joe").match({age: value => value > 15,address:{ city: "Seattle"}},value => console.log("cb1",value));

Lodash differenceBy alternative

I am trying to find a way to differentiate between 2 object literals by a property from each.
I can get this working by using 2 ._forEach()
_.forEach(forms, form => {
_.forEach(this.sections, section => {
if(form.section === section.name){
section.inProgress = true;
}
});
});
However this does not seem ideal. I have tried with the _.differenceBy
_.differenceBy(forms, this.sections, 'x');
But I cant differentiate by the property name as the "name" that i want to check by is name in one of the arrays and section in the other.
I want to use something like
_.differenceBy(forms, this.sections, 'section' === 'name');
It there something in lodash for this?
You can use _.differenceWith() with a comparator that refers to a specified object property in each array:
var forms = [{ 'name': 'somethingA' }, { 'name': 'somethingB' }];
var sections = [{ section: 'somethingB' }];
var result = _.differenceWith(forms, sections, function(arrValue, othValue) {
return arrValue.name === othValue.section;
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

How to chain get and map the result with lodash?

I've got a list I'm trying to pull an object from using _.get but following that selection I need to loop over the object to create a new property. So far I've been successful using a combination of _.get and _.map as shown below but I'm hoping I can use _.chain in some way.
var selected = _.get(results, selectedId);
return _.map([selected], result => {
var reviews = result.reviews.map(review => {
var reviewed = review.userId === authenticatedUserId;
return _.extend({}, review, {reviewed: reviewed});
});
return _.extend({}, result, {reviews: reviews});
})[0];
Is it possible to do a transform like this using something other than map (as map required me to break this up/ creating an array with a solo item inside it). Thank you in advance!
I can see that you're creating unnecessary map() calls, you can simply reduce all those work into something like this:
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
The defaults() method is similar to extend() except once a property is set, additional values of the same property are ignored.
var selectedId = 1;
var authenticatedUserId = 1;
var results = {
1: [
{ userId: 1, text: 'hello' },
{ userId: 2, text: 'hey' },
{ userId: 1, text: 'world?' },
{ userId: 2, text: 'nah' },
]
};
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
document.body.innerHTML = '<pre>' + JSON.stringify(output, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>