I'm a junior in js development so this might be a silly question, so apologies.
I'm using firebase in my app to store my strings, I call it once when the app is loaded and keep it in memory for faster access.
Then I use strings.find to find a specific string in the array. All work find when running on iOS, but when running on Android I keep getting this weird error
TypeError: Undefined is not a function (evaluting 'this.strings.find...
Here's my data schema
{'strings': [{'name':'something', 'value':'something'} ... ]
And here's my code
getString(name) {
return this.strings.find(x => x.name == name).value
}
And this is where I define the object
init(onUpdated) {
Promise.all([stringsDb.once('value'), multiLang.once('value')])
.then(([stringsSnapshot, multiLangSnapshot]) => {
this.strings = stringsSnapshot.value
this.multiLang = multiLangSnapshot.value
onUpdated(true)
})
.catch((err) => {
console.log(err.stack)
onUpdated(false)
});
}
This probably happens because the this.strings is not an array. you need to debug it and see if its actually an array or not, my guess is it returns an object, if so then you need to use a reducer or perhaps Object.values or something to convert to an array.
p.s you should use the === operator when comparing the strings
Related
I'm working with Javascript to build a mapping function, that, given a scheme should find the mapper's object's value within an application variable.
My attempt at doing this results in an undefined error and I'm trying to figure out what I need to change to make it work correctly, I've put together a JS fiddle that can be found here as well.
const application = {
state: {
outgoings: {
credit: 0,
food: 0
}
}
}
const mapper = {
"AppFoodSpend": "outgoings.food",
"AppCreditSpend": "outgoings.credit"
}
for (const field in mapper) {
console.log(application.state[mapper[field]])
// expected results should be credit 0 and food 0
}
For further context, I have an object called application which contains some fields, they may not always be in the same order. My mapper object contains some key/value pairs, and it's the value that I'd like to try and find in application. for for instance, my console log should be retrieving something like:
application.state.outgoings.food
application.state.outgoings.credit
They may not always be in this order, so I need to (on each iteration of the mapper) find the key in my application variable.
What do I need to change here?
Strings like 'outgoings.food' are not valid to navigate a data structure. You could use some "lenses" lib or write something like this...
const mapper = {
"AppFoodSpend": ["outgoings", "food"],
"AppCreditSpend": ["outgoings", "credit"]
}
for (const field in mapper) {
console.log(
mapper[field].reduce(
(ctx, path) => ctx[path],
application.state
)
)
}
I was trying to check whether a string is present in another string.I am using the below code
expect(actualTimeZone).to.include(employee.timeZone);
But it is giving the below error
AssertionError: object tested must be an array, a map, an object, a set, a string, or a weakset, but object given
But when I tried with the below code, it is not throwing the above error
expect(actualTimeZone).to.eventually.equal(employee.timeZone);
I am working on protractor framework with cucumber and javascript. Could someone help to solve this issue
I tried using the below code and it is working
expect(actualTimeZone).to.eventually.include(employee.timeZone);
The error:
object tested must be an array, a map, an object, a set, a string, or
a weakset, but object given
happens when you are returning a set of web elements (which is an object not an array) and you try to treat them like an regular array.
So, first you have to extract the array from the object, then map each element getting the desire attribute, and finally filtering to prevent empty or null values.
Below an example step by step, using cucumber and JavaScript in Cypress.
Cypress.Commands.add('getTableData', () => {
// This is the table in the front-end, we read it completely and save it in an alias called table
cy.get('[data-test="table-body"]').find('tr').as('table')
// Now we apply the logic explained before
cy.get('#table').then(row => {
let arr = Object.keys(row)
return arr.map(element => { return row[element].textContent })
.filter(element => element)
})
})
Now, we can use the method above and use it to execute any expect like .includes (which was your issue)
And('Verify the value is in the table', () => {
cy.getTableData().then(arr => {
expect(arr).includes(assetName)
})
})
Also, with the same method we can loop through the entire array and do anything with each element:
And('Verify the value is in the tablex', () => {
cy.getTableData().each(element => {
cy.log(element)
})
})
I have the following code to load data from my firebase which works but it is painfully ugly.
loadData() {
// app.component
this.loadJobCodeSubscription = this.databaseService.getData()
.subscribe(result => {
this.data = null;
var data_raw: Array<any>;
if (Object.keys(result)[0] != '$value') {
data_raw = Object.keys(result).map(function (key) {
return [key, result[key]['firstName'], result[key]['lastName']];
})
// remove extra data downloaded from firebase
jobDataRaw.pop();
jobDataRaw.pop();
}
this.jobCodeData = jobDataRaw;
}, error => {
console.log('Error downloading job codes', error)
})
}
// DatabaseService
getData() {
return this.af.database.object('/jobCodes/' + this.currentUser.company)
}
What happens is that if the branch I am querying does not have data, my "result" will display
Andi if I have data coming back, I will get something like
The only difference (that I can think of to check for) between these two data is that one has a $value key and one does not
That is why I am using that weird if statement to check if that key exists.
Is there a way to check for data in a neater way?
Also, is there a way to cast 'result' to the right format as oppose to 'any' which it currently is
Note. I am using Angular 2 with AngularFire2
First, presuming you have control of the backend, you could alter the return to be more easily understandable. Returning an empty payload is a bit confusing if you really mean "nothing".
Second, dangerous to do this line:
Object.keys(result)[0]
As Object.keys(result) may not be the same in the future, even though it may be deterministic.
I think the safest way to do this would be:
if (Object.keys(result).every(k => k != '$value')) {
That being said, I don't think there's an easier way to determine that given the information you've presented. If you continue with this approach, it would be good to make sure you are guarding your statements carefully, as it seems like
bad data could slip through in the future.
Turns out that if I change the return type of the observable to be the original firebase snapshot, the data looks much better. It allows me to call if(snapshot.val()){}
return this.af.database.object('/jobCodes/' + this.currentUser.company, {preserveSnapshot: true})
I've got following piece of code:
if (Array.isArray(value.items)) {
return Array.prototype.concat.apply(items, value.items.map(function(item, key) {
return traverseCallback(item, path.concat(['items', key]), resolve);
}));
}
that is throwing an error on value.items.map. The error is:
Property 'map' does not exist on type 'IObjectSchema | IObjectSchema[]'.
The thing is - I deal either with a IObjectSchema element or an array of such elements. When I go down into Array.isArray... then I'm 100% sure I've got an array, hence I've got .map function available on the array. This is obvious JS.
However, TypeScript seems unconvinced that it has the .map available, probably, it doesn't recognize Array.isArray... call one level up.
The question is - how can I use a variable as if it was either a typed element or a typed element array and allow to use .map if it is an array? I'm sure my code is JS-correct, but TS throws errors here.
The compiler is right about not having the map method on type IObjectSchema | IObjectSchema[], so you need to tell it that after the if it's safe to assume that you're dealing with IObjectSchema[].
You have two options:
(1) Cast
if (Array.isArray(value.items)) {
return Array.prototype.concat.apply(items, (value.items as IObjectSchema[]).map(function(item, key) {
return traverseCallback(item, path.concat(['items', key]), resolve);
}));
}
(2) Use type guards
if (value.items instanceof Array) {
return Array.prototype.concat.apply(items, value.items.map(function(item, key) {
return traverseCallback(item, path.concat(['items', key]), resolve);
}));
}
Seems like what you did is similar to my 2nd solution, but the compiler doesn't recognize the type guard when using Array.isArray.
You can only use typeof or instanceof.
You can also create your own type guard if you still want to use Array.isArray:
function isObjectSchemaArray(items: IObjectSchema | IObjectSchema[]): items is IObjectSchema[] {
return Array.isArray(items);
}
And then:
if (isObjectSchemaArray(value.items)) { ... }
I am working with a mobile App which uses Parse as a backend and I have an issue with the find function. When running the find function in the format of:
var = firstQuery = (new Parse.Query("MyParseObject"))
.find(),
secondQuery = (new Parse.Query("OtherParseObject")).get(id)
// there is only one object that firstQuery can find
Parse.Promise.when(firstQuery, secondQuery)
.then( function (query1res, query2res) {
// query1res should return only one result wrapped in an array,
// instead query1res is an object without a get method
query1res.forEach (function (res) {
// this fails: cannot get .length of undefined
})
// ... do stuff with the returned data
})
Is there something i am missing? I am sure this used to work before, but now it does not.
It is quite difficult to correctly get in an debug this issue thanks to the way Parse works, but their docs outline that this should return an array, but it does not at this point of time.
Thanks for your help.
Based on the Parse docs, it looks like Parse.Promise.when expects an array, although based on this support thread, the results will be passed in as individual arguments to the then handler. Give this a try:
Parse.Promise.when([firstQuery, secondQuery])
.then(function (query1res, query2res) {
// use query1res and query2res
});
Turns out it was down to a function deeper in the code, which needed to return a promise to chain off of. after adding this the code was happy enough. The function that needed to return the promise was called in the forEach and has nothing to do with the initial two queries, which is what was throwing me.