I am writing program that is going to help me at work. This part of code is responsible for showing new window with ticket id when there is a "YES" in Warn Owner. Code works fine, yet it only shows first ID from array "found". How I can make it display all stuff?
I Tried this one with .filter yet I was unable to make it work.
if(!isInited) {
ownerNotifyBaseArray = data.map((value) => ({
id: value[dataTicketID],
owner: value[dataWarnOwner],
company: value[dataCompanyName]
}));
} else {
const newOwnerNotifyBaseArray = data.map((value) => ({
id: value[dataTicketID],
owner: value[dataWarnOwner],
company: value[dataCompanyName]
}));
const differences =
_.differenceWith(newOwnerNotifyBaseArray,ownerNotifyBaseArray,_.isEqual)
console.log('differences', differences)
var found = differences.find(z => z.owner === 'Yes');
if(found) {
newWindow = window.open("", "WarnOwner", "width=600,height=200");
newWindow.document.write('<p>Hey! You have a new warn owner on: <b>'+
(found.id)+'</b> for '+(found.company)+'</p>');
newWindow.focus()
//})
ownerNotifyBaseArray = newOwnerNotifyBaseArray
}
ownerNotifyBaseArray = newOwnerNotifyBaseArray
}
New window should contain all entries in array found
Related
I am using React with nextJS to do web developer,I want to render a list on my web page, the list information comes from the server(I use axios get function to get the information). However some JSON objects are lack of some information like the name, address and so on. My solution is to use a If- else to handle different kind of JSON object. Here is my code:
getPatientList(currentPage).then((res: any) => {
console.log("Response in ini: " , res);
//console.log(res[0].resource.name[0].given[0]);
const data: any = [];
res.map((patient: any) => {
if ("name" in patient.resource) {
let info = {
id: patient.resource.id,
//name:"test",
name: patient.resource.name[0].given[0],
birthDate: patient.resource.birthDate,
gender: patient.resource.gender,
};
data.push(info);
} else {
let info = {
id: patient.resource.id,
name: "Unknow",
//name: patient.resource.name[0].given[0],
birthDate: patient.resource.birthDate,
gender: patient.resource.gender,
};
data.push(info);
}
});
Is there any more clever of efficient way to solve this problem? I am new to TS and React
Use the conditional operator instead to alternate between the possible names. You should also return directly from the .map callback instead of pushing to an outside variable.
getPatientList(currentPage).then((res) => {
const mapped = res.map(({ resource }) => ({
id: resource.id,
// want to correct the spelling below?
name: "name" in resource ? resource.name[0].given[0] : "Unknow",
birthDate: resource.birthDate,
gender: resource.gender,
}));
// do other stuff with mapped
})
I have state set as follow
const [stories, setStories] = useState([]);
I fetch Data from API in array, and i map the array and set the using setStories as:
setStories(prevState => prevState.concat({user: {name: 'XYZ', profile: 'ABC', stories: [{id: 1, image: 'testing'}];
The above codes are working fine, but i am stuck, when i have to concat the latest story if the id did not matched with fetched data. I have tried below solution but it didnot help:
stories.map(story => {
if(story && story.hasOwnProperty(key)){
//where above key is the user key fetched from the another API, i.e., user key
story?.[key].stories.map(storedStory =>
id(storedStory.id !== fetchedStory.id){
story?.[key].stories.concat({story})}
but the above code did not work, as it only mutate the state and is avoiding re-rendering.
Looking for a clean and efficient method to overcome this. THanks
It's hard to tell what you're trying to accomplish without seeing a full example. But I think your main problem is that you're not using the returned value from map, and from the naming it looks like you're appending the wrong element.
It will help to simplify first.
const newState = stories.map(story => {
if (story?.hasOwnProperty(key)) {
const found = story[key].stories.find(s => s.id === fetchedStory.id);
if (found) {
return story;
} else {
// Let's make a new object with the fetchedStory
// appended into THIS user's stories
return {
...story,
[key]: {
...story[key],
stories: [
...story[key].stories,
// This is supposed to be fetchedStory
// not `story` right??
fetchedStory,
]
}
}
}
} else {
return story;
}
});
setStory(newState);
Edit: You're having a hard time expressing your business logic, and the complexity of the data structure is not helping. So keep simplifying, encapsulate the complex syntax into functions then express your business logic plainly. Ie,
const appendStory = (originalObject, userId, storyToAppend) => {
return {
...originalObject,
[userId]: {
...originalObject[userId],
stories: [
...originalObject[userId].stories,
storyToAppend,
]
}
}
};
const userExistsInList = (users, user) => {
return users?.hasOwnProperty(user);
}
const newStoryAlreadyInStories = (stories, newStory) => {
return stories.find(s => s.id === newStory.id);
}
const newState = stories.map(story => {
if (userExistsInList(story, key)) {
const found = newStoryAlreadyInStories(story[key].stories, fetchedStory);
if (found) {
// User is already in state and the new story is already in the list
// Add business logic here
} else {
// User is already in state and the new story
// is not in their list
// Add business logic here
}
} else {
// User is not in the list yet
// Add business logic here
}
});
I need to be able to receive data from an external API and map it dynamically to classes. When the data is plain object, a simple Object.assign do the job, but when there's nested objects you need to call Object.assign to all nested objects.
The approach which I used was to create a recursive function, but I stumble in this case where there's a nested array of objects.
Classes
class Organization {
id = 'org1';
admin = new User();
users: User[] = [];
}
class User {
id = 'user1';
name = 'name';
account = new Account();
getFullName() {
return `${this.name} surname`;
}
}
class Account {
id = 'account1';
money = 10;
calculate() {
return 10 * 2;
}
}
Function to initialize a class
function create(instance: object, data: any) {
for (const [key, value] of Object.entries(instance)) {
if (Array.isArray(value)) {
for (const element of data[key]) {
// get the type of the element in array dynamically
const newElement = new User();
create(newElement, element)
value.push(newElement);
}
} else if (typeof value === 'object') {
create(value, data[key]);
}
Object.assign(value, data);
}
}
const orgWithError = Object.assign(new Organization(), { admin: { id: 'admin-external' }});
console.log(orgWithError.admin.getFullName()); // orgWithError.admin.getFullName is not a function
const org = new Organization();
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
create(org, data);
// this case works because I manually initialize the user in the create function
// but I need this function to be generic to any class
console.log(org.users[0].getFullName()); // "name surname"
Initially I was trying to first scan the classes and map it and then do the assign, but the problem with the array of object would happen anyway I think.
As far as I understand from your code, what you basically want to do is, given an object, determine, what class it is supposed to represent: Organization, Account or User.
So you need a way to distinguish between different kinds of objects in some way. One option may be to add a type field to the API response, but this will only work if you have access to the API code, which you apparently don't. Another option would be to check if an object has some fields that are unique to the class it represents, like admin for Organization or account for User. But it seems like your API response doesn't always contain all the fields that the class does, so this might also not work.
So why do you need this distinction in the first place? It seems like the only kind of array that your API may send is array of users, so you could just stick to what you have now, anyway there are no other arrays that may show up.
Also a solution that I find more logical is not to depend on Object.assign to just assign all properties somehow by itself, but to do it manually, maybe create a factory function, like I did in the code below. That approach gives you more control, also you can perform some validation in these factory methods, in case you will need it
class Organization {
id = 'org1';
admin = new User();
users: User[] = [];
static fromApiResponse(data: any) {
const org = new Organization()
if(data.id) org.id = data.id
if(data.admin) org.admin = User.fromApiResponse(data.admin)
if(data.users) {
this.users = org.users.map(user => User.fromApiResponse(user))
}
return org
}
}
class User {
id = 'user1';
name = 'name';
account = new Account();
getFullName() {
return `${this.name} surname`;
}
static fromApiResponse(data: any) {
const user = new User()
if(data.id) user.id = data.id
if(data.name) user.name = data.name
if(data.account)
user.account = Account.fromApiResponse(data.account)
return user
}
}
class Account {
id = 'account1';
money = 10;
calculate() {
return 10 * 2;
}
static fromApiResponse(data: any) {
const acc = new Account()
if(data.id) acc.id = data.id
if(data.money) acc.money = data.money
return acc
}
}
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
const organization = Organization.fromApiResponse(data)
I can't conceive of a way to do this generically without any configuration. But I can come up with a way to do this using a configuration object that looks like this:
{
org: { _ctor: Organization, admin: 'usr', users: '[usr]' },
usr: { _ctor: User, account: 'acct' },
acct: { _ctor: Account }
}
and a pointer to the root node, 'org'.
The keys of this object are simple handles for your type/subtypes. Each one is mapped to an object that has a _ctor property pointing to a constructor function, and a collection of other properties that are the names of members of your object and matching properties of your input. Those then are references to other handles. For an array, the handle is [surrounded by square brackets].
Here's an implementation of this idea:
const create = (root, config) => (data, {_ctor, ...keys} = config [root]) =>
Object.assign (new _ctor (), Object .fromEntries (Object .entries (data) .map (
([k, v]) =>
k in keys
? [k, /^\[.*\]$/ .test (keys [k])
? v .map (o => create (keys [k] .slice (1, -1), config) (o))
: create (keys [k], config) (v)
]
: [k, v]
)))
class Organization {
constructor () { this.id = 'org1'; this.admin = new User(); this.users = [] }
}
class User {
constructor () { this.id = 'user1'; this.name = 'name'; this.account = new Account() }
getFullName () { return `${this.name} surname`}
}
class Account {
constructor () { this.id = 'account1'; this.money = 10 }
calculate () { return 10 * 2 }
}
const createOrganization = create ('org', {
org: { _ctor: Organization, admin: 'usr', users: '[usr]' },
usr: { _ctor: User, account: 'acct' },
acct: { _ctor: Account }
})
const orgWithoutError = createOrganization ({ admin: { id: 'admin-external' }});
console .log (orgWithoutError .admin .getFullName ()) // has the right properties
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
const org = createOrganization (data)
console .log (org .users [0] .getFullName ()) // has the right properties
console .log ([
org .constructor .name,
org .admin .constructor.name, // has the correct hierarchy
org .users [0]. account. constructor .name
] .join (', '))
console .log (org) // entire object is correct
.as-console-wrapper {min-height: 100% !important; top: 0}
The main function, create, receives the name of the root node and such a configuration object. It returns a function which takes a plain JS object and hydrates it into your Object structure. Note that it doesn't require you to pre-construct the objects as does your attempt. All the calling of constructors is done internally to the function.
I'm not much of a Typescript user, and I don't have a clue about how to type such a function, or whether TS is even capable of doing so. (I think there's a reasonable chance that it is not.)
There are many ways that this might be expanded, if needed. We might want to allow for property names that vary between your input structure and the object member name, or we might want to allow other collection types besides arrays. If so, we probably would need a somewhat more sophisticated configuration structure, perhaps something like this:
{
org: { _ctor: Organization, admin: {type: 'usr'}, users: {type: Array, itemType: 'usr'} },
usr: { _ctor: User, account: {type: 'acct', renameTo: 'clientAcct'} },
acct: { _ctor: Account }
}
But that's for another day.
It's not clear whether this approach even comes close to meeting your needs, but it was an interesting problem to consider.
I am trying to delete item from array in profile collection named items subcollection. Function should be triggered whenever item is deleted from main items collection. Problem with below function is that when triggered it deletes all items from profile instead of deleted one only. How I could iterate over items array and perform check of deleted item id.
exports.updateDeletedItemOnProfile = functions.firestore
.document('items/{itemId}')
.onDelete((snap, context) => {
const { itemId } = context.params
const deletedItem = snap.data()
if(deletedItem){
db.collection('profiles')
.doc(deletedItem.user.id)
.update({
items: admin.firestore.FieldValue.delete({
id: itemId,
title: deletedItem.title,
price: deletedItem.price,
image: deletedItem.image
})
})
}
return true
})
onDelete triggers only fire when an entire document was deleted. By this time, it's too late - the document is simply gone. Something chose to delete it, and you won't be able to tell what did that.
If you want a function to trigger when any part of a document is modified, you should use an onUpdate trigger instead. That will give you the entire contents of the document before and after the change. You will have to compare them to figure out what specifically changed.
I 've also tried onUpdate trigger for deleting data along with onWrite trigger to listen for any change when data is changed, however in both cases it does not give me any result for part referring to deleting data. Items subcollection in profiles collection remains unchanged. I am not sure where error in function lies, function execution does not show any error, finishes with status Ok....
exports.updateDeletedItemOnProfile = functions.firestore
.document('items/{itemId}')
.onUpdate((change, context) => {
const { itemId } = context.params
const beforeItem = change.before.data()
const updatedItem = change.after.data()
if (updatedItem === null) {
return db.collection('profiles')
.doc(beforeItem.user.id)
.delete({
items: admin.firestore.FieldValue.arrayRemove({
id: itemId,
title: beforeItem.title,
price: beforeItem.price,
image: beforeItem.image,
status: beforeItem.status,
})
})
}
return true
})
exports.updateDeletedItemOnProfile = functions.firestore
.document('items/{itemId}')
.onUpdate((change, context) => {
const { itemId } = context.params
const beforeItem = change.before.data()
const updatedItem = change.after.data()
if (updatedItem === null) {
return db.collection('profiles')
.doc(beforeItem.user.id)
.update({
items: admin.firestore.FieldValue.delete({
id: itemId,
title: beforeItem.title,
price: beforeItem.price,
image: beforeItem.image,
status: beforeItem.status,
})
})
}
return true
})
I'm working on a project that is a basic message app. basically i have created an array of objects that allows me to see pre built messages. Then i should be able to click a clear all button and clear all of the messages that are being displayed by looping through the array of objects. this is what i have so far in my messageData.js
const messages = [
{
id: 'message1',
message: 'Hello everyone! Welcome to hell',
userId: 'user1',
},
{
id: 'message2',
message: 'Yall are weirdos!',
userId: 'user3',
},
{
id: 'message3',
message: 'Hey! I think everyone is awesome!',
userId: 'user2',
},
{
id: 'message4',
message: 'Thanks for saying that my friend.',
userId: 'user4',
},
{
id: 'message5',
message: 'Hey buddy, what is up?',
userId: 'user4',
},
];
const getMessages = () => messages;
and what i want to do is basically on click allow the messages key value to be changed to an empty string onclick so that i get rid of the displayed messages without getting rid of the object so that i can later push new messages into these key values.
I started to write this but i seem to be missing something..
const clearBtnFunction = () => {
messages.splice(1, '');
};
i'll be calling the event listener on my main.js file so i'm not super worried about that part yet. I just want to know the proper syntax for replacing the key value in the array if thats possible.
Here is what i opted for. I placed this function, not in the messagesData.js but in the messages.js where i'm building the domstring
const clearBtnFunction = (e) => {
e.preventDefault();
const messages = message.getMessages();
messages.splice(0, messages.length);
messageBuilder(messages);
};
const clearBtnFunction = () => {
messages.foreach( ( message ) => {
message.message = "";
});
};
or with a for loop
const clearBtnFunction = () => {
for( let i =0; i < messages.length; i++) {
messages[i].message = "";
}
};