Data getting added suspiciously after Firebase call - javascript

I am struggling with a uncertain issue and not able to figure out what can be the root cause.
I am calling Firebase for data using the below code:
public getAllObject(filters: Filter[]): Observable<T[]> {
return this.afs
.collection('somecollection')
.snapshotChanges()
.pipe(
take(1),
map((changes) => {
return changes.map((a) => {
console.log(a.payload.doc.data()) ==============> displays everything is alright
const data: any = a.payload.doc.data() as T;
const code = a.payload.doc.id;
return { code, ...data } as T;
});
})
);
}
and I am consuming the above service mentioned below:
this.service.getAllObject(this.service.getFilters()).subscribe(
(entities) => {
console.log(entities);==============> display's array and things are wrong.
},
(error) => {
console.error(error);
}
)
Problem description
When I call the above API, I get an array of below objects. Problem is with the stores attribute. As we move forward in the array the stores attribute contains values from pre elements. This phenomenon is HAPPENING ON CLIENT SIDE only.
I was wondering if I am using attribute name 'stores' which is any kind of reserved keyword which is causing this to happen. Or i am using rxjs in wrong way.
Current results
{
code: 123,
stores: { abc }
},
{
code: 345,
stores: { def, abc }
},
{
code: 678,
stores: { xyz, def, abc }
},
Expected results
getAllObject console.log displays the following which is correct
{
code: 123,
stores: { abc }
},
{
code: 345,
stores: { def }
},
{
code: 678,
stores: { xyz }
},
Current analysis
console.log(a.payload.doc.data()); ====> Showing correct
const data: any = a.payload.doc.data();
const code: string = a.payload.doc.id;
console.log({ code, ...data });
return { code, ...data } as T; =====> Showing INCORRECT and adding stores from earlier element to current one.

Related

How to solve undefined error when trying to access a nested array in a React state variable?

I am trying to build a form in which you can dynamically add fields and subfields. As an example, my form looks like this:
Store information:
Name
Items inventory: [] (this is the nested array part,
you press a button and can add another item to the inventory)
***Buttons to add more items to this store***
***Buttons to add more stores***
The code I am using to achieve this looks like this. I have updated to reflect the answers that I got so far and also the proper syntax for (there was an issue with the closing tags)
function StoreForm() {
const [storeInputs, setStoreInputs] = useState([
{ storeName: "", items: [{ itemName: "" }] },
]);
///...
return (
{storeInputs.map((store,index) =>
{
//input data here
{store.items.map((item,i) =>
{
//This is where it throws the undefined error
//do something
}
)
}
}
)
)
}
The above code now works on the first run, but when I try to add another store to the form, it throws the undefined error again. Here is the code I am using for the buttons that add another store:
const handleRemoveStore = (index) => {
const list = [...storeInputs];
list.splice(index, 1);
setItemsInputs(list);
};
const handleAddStore = () => {
setItemsInputs([...storeInputs, { storeName: "", items: [{ itemName: "" }] }]);
};
Thank you for the answers thus far!
On
return (
{storeInputs.map(store,index) =>
{
//input data here
{storeInputs.items.map(item,i) =>
{
//This is where it throws the undefined error
//do something
}
}
}
)
You map two time on storeInputs, you need to do someting like :
return (
{storeInputs.map((input,index) =>
{
//input data here
{input.items.map((item,i) =>
{
//This is where it throws the undefined error
//do something
}
}
}
)
Be careful about the () around the params, you do :
x.map(a, index) => {})
In fact is :
x.map((a, index) => {})
The first ( is for the map methods, second is for the params of the function inside your map.
The storeInputs state hasn't any items property to map in the inner mapping. That property belongs to the store object mapped from the outer loop.
function StoreForm() {
const [storeInputs, setStoreInputs] = useState([
{ storeName: "", items: [{ itemName: "" }] },
]);
...
return storeInputs.map((storeInput, index) => {
// input data here
return (
...
{storeInput.items.map((item, i) => { ... })}
...
);
});
}

filter function shows an empty array

I want to create a search filter. The way it works is a user inputs a text in the search bar, the input is stored using vuex and the result is shown in a different page. Here's an array of objects in a js file
export const productData = [
{
id: 1,
name: "table",
materials: "wood"
},
{
id: 2,
name: "table2",
materials: "metal"
},
{
id: 3,
name: "chair",
materials: "plastic"
}
]
I want to filter using the user's input. Here's my function
import { productData } from '#/data/productData'
export default {
data() {
return {
products: productData
}
},
computed: {
userInput() {
return this.$store.state.userInput
},
filterProducts: function() {
return this.products.filter(q => q.name.match(this.userInput))
}
}
}
When I console the userInput, it works fine! So the problem is in the filterProducts function. It shows an empty array if I console it. What am I doing wrong? Thank you.
edit: the reason I make a new variable called products is because the actual js file is more complex so I had to flatten the array. But the flatten process works fine so I thought I would just simplify the question.
The match function accepts a regex, not String. Give indexOf a try:
filterProducts: function() {
return this.products.filter(q => q.name.indexOf(this.userInput) >= 0)
}

Setting the property of one specific key in an object in a reduce is setting it for all properties

Okay, I have literally no idea whats going on here. I'm assuming its some kind of reference issue? But I dont know how to get around it or whats causing it.
To sum it up, I have a list of objects, as well as an object that gets prepopulated to make sure I have data for all keys in the object.
I need to iterate over this list of objects, and by using the timeframeId in the metadata object, and the id in the data object, I want to assign the entire data object to the corresponding timeframeId and id hierarchy in the prepopulated object.
For some reason, all data properties are being overwritten to whatever the last row data is.
I've linked a repl so you can see for yourself: https://repl.it/#ThomasVermeers1/UnwrittenNoisyFirm#index.js
But my code is as follows:
const buildSegmentsFromRows = (rows, timeframeMetadata, defaultSegmentData) => {
// Prepopulate object to make sure every timeframe has a 'hello' key in it with some data
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
segments: defaultSegmentData,
};
return segmentMap;
}, {});
// Now simply just loop over the rows, and set [row.metadata.timeframeId].segments[row.data.id] to row.data
const segments = rows.reduce((partialSegments, row) => {
const { timeframeId } = row.metadata;
const { id } = row.data;
/**
* This is the line where everything goes wrong
*/
partialSegments[timeframeId].segments[id] = row.data;
return partialSegments;
}, emptySegments);
return segments;
};
const rows = [
{
metadata: { timeframeId: '20202_01' },
data: {
'id': 'hello', 'value': 15
}
},
{
metadata: { timeframeId: '20202_02' },
data: {
'id': 'hello', 'value': 10
}
}
]
const timeframemetadata = [
{ timeframeId: '20202_01'},
{ timeframeId: '20202_02'}
]
const defaultSegmentData = {
'hello': {
'id': 'hello',
}
}
console.log(JSON.stringify(buildSegmentsFromRows(rows, timeframemetadata, defaultSegmentData), null, 2))
I'm expecting the end result to be:
{
"20202_01": {
"metadata": {
"timeframeId": "20202_01"
},
"segments": {
"hello": {
"id": "hello",
"value": 15
}
}
},
"20202_02": {
"metadata": {
"timeframeId": "20202_02"
},
"segments": {
"hello": {
"id": "hello",
"value": 10
}
}
}
}
But instead, value is getting set to 10 in all instances. I'm thinking its because we're setting the property to row.data, which is a reference, and gets updated on every call? But I'm at a complete loss here.
The problem is that you are referring to the same object for every segments in the list.
Therefore, changing the value of segments[id] will update defaultSegmentData, causing every reference to defaultSegmentData to change as well.
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
segments: defaultSegmentData, // Everything goes wrong here.
};
return segmentMap;
}, {});
A simple solution to this problem is to avoid using the same reference to the object when creating the segmentMap:
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
/** Or whatever default value you want.
* Just make sure to create a new instance of it for each call.
*/
segments: {},
};
return segmentMap;
}, {});

Working with a message having repeated field

My Proto file looks like this -
message Todos {
repeated Todo todos = 1;
}
message Todo {
int32 id = 1;
string name = 2;
bool done = 3;
}
It comes works fine when I send {todos: [...]} from the server, but gets an empty object {} when directly sending an array.
Server
getAll(_, callback) {
console.log(todos);
return callback(null, { todos });
}
Client
client.getAll({}, function (err, res) {
if (err) {
return console.log(err);
}
console.log('todos: ');
return console.log(res);
});
Versions -
#grpc/proto-loader - ^0.1.0
grpc - ^1.13.0
In my case I was trying to return an array and it seems you always have to return an object....
hero.proto
syntax = "proto3";
package hero;
service HeroService {
rpc GetHeroById(HeroById) returns (Hero) {}
rpc ListHeroesById(HeroById) returns (HeroList) {}
}
message HeroById {
int32 id = 1;
}
message Hero {
int32 id = 1;
string name = 2;
}
message HeroList {
repeated Hero heroes = 1;
}
hero.controller.ts
#GrpcMethod('HeroService')
listHeroesById(data: HeroById, metadata: any): object {
const items = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Doe' },
{ id: 3, name: 'Billy' },
];
// make sure you return an object, even if you want an array!
return { heroes: items.filter(({ id }) => id === data.id) };
}
Check out my example TypeScript project here:
https://github.com/kmturley/angular-nest-grpc
If I understand correctly, you are having problems if you send just the array todos, instead of an object containing that array. Sending just the array is simply an invalid use of the API. Protobuf services always send protobuf messages, so you have to pass an actual message object, and not a single field of that object.
If you use grpc.load then you can send back an array:
callback(null, todos);
If you use protoLoader.loadSync and grpc.loadPackageDefinition, then you need to send back:
callback(null, { todos: todos });

Match two values in a collection

I have two values I need to match as you can see in the picture below:
I tried something like this:
const index = state.locks.users.findIndex(
stateUser => stateUser._id === action.payload.customerPayload.accessid
But I’m getting the error:
findIndex of undefined.
And I guess that’s because of locks being an array.
But I’m really uncertain how to fix this issue. Should I have multiple findIndexes? One for the lock and one for to match the users?
Thanks for reading my post. And I appreciate all the help I can get.
The code snippet should be
let itemIndex = -1;
state.locks.map((lock) => {
lock.users.findIndex(...)
});
Assuming state is an object containing locks array.
My suggestion to you is do a double for loop (as you've already figured) to get the user object that you need.
Consider the following snippet (adjust to your data structure):
let state = {
locks: [
{
users: [
{ _id: '123' },
{ _id: '456' }
]
},
{
users: [
{ _id: '678' },
{ _id: '789' }
]
},
]
};
function getUserObjByID(stateObj, userID) {
for (let usersObject of state.locks) {
for (let user of usersObject.users) {
if (user._id === userID) {
return user;
}
}
}
}
let myObj = getUserObjByID(state, '678');
console.log(myObj);
So it works now. What I had to do with my reducer was this:
case 'REMOVE_USER':
return {
...state,
locks: state.locks.map(lock => {
return {
...lock,
users: lock.users
? lock.users.filter(
user => user._id != action.payload.customerPayload.accessid
)
: []
}
})
}

Categories