how to merge arrays from 2 different arrays - javascript

this is an image from the API that returns the test results before and after each other.
Initially when the user has not entered the test result, the array result = null, After the user enters, the array will have the same result as the image below:
my problem is after user input test, and then we update field result_template can be add or remove subjects so how do i merge subjects if use edit again result_template,
If there are new subjects but no scores are available, the default is set = 0
You can watch the video for better understanding: link
desired result: image
here is my code:
const { data } = await this.$store.dispatch(
`${UserGeneral.STORE_ADMIN_USER_KEY}/getDetailSchoolResults`,
this.$route.params.id
);
const formData = data.data;
if (formData.result) {
const listResultTemplate = formData.result.result.map((item) => {
let data = {
title: item.title,
subjects: [],
files: [],
};
item.files.forEach((file) => {
data.files.push(file);
});
item.subjects.forEach((subject) => {
data.subjects.push({
name: subject.name,
scores: {
total_score: subject.scores.total_score,
medium_score: subject.scores.medium_score,
private_score: subject.scores.private_score,
},
});
});
return data;
});
this.result = listResultTemplate;
} else {
const listResultTemplate = formData.result_template.result_template.map(
(item) => {
let data = {
title: item.title,
subjects: [],
files: [],
};
item.subjects.forEach((subject) => {
data.subjects.push({
name: subject,
scores: {
total_score: 0,
medium_score: 0,
private_score: 0,
},
});
});
return data;
}
);
this.result = listResultTemplate;
}
thanks for your help !

Related

How to implement the display of icons with links to the social network?

I have a small project with user cards.
I have a JSON file with data on users whose cards are generated.
My task is in the card to show the icons of those social networks that the user has. If the user does not have any of the social networks - do not show the icon of this social network in his profile.
Frankly, I do not understand how to do it.
The data in JSON come in this form (on the example of one user):
{
"id": 7,
"firstName": "Daniel",
"lastName": "Day-Lewis",
"profilePicture": "https://i2.wp.com/comicbookdebate.com/wp-content/uploads/2019/07/There20will20be20blood202-750x460.jpg?resize=750%2C460&ssl=1",
"contacts": [
"https://www.facebook.com/JasonStatham/",
"https://twitter.com/realjstatham",
"https://www.instagram.com/jasonstatham/?hl=ru"
]
}
JS index file (add so that it is clear how I work with a layout in this project):
'use strict';
const cardsContainer = document.querySelector('#root');
async function loadCards(url) {
try {
const response = await fetch(url);
const data = await response.json();
const cards = data.map((data) => generateUserCard(data));
cardsContainer.append(...cards);
} catch(e) {
throw new Error(e);
}
}
loadCards('./assets/js/data.json');
function generateUserCard(userObj) {
const fullName =
`${userObj.firstName} ${userObj.lastName}`.trim() ||
CARD_CONSTANTS.userName;
const imgWrapper = createUserCardImageWrapper(userObj, fullName);
const cardName = createElement('h2', {classNames: ['cardName']}, fullName);
const cardDescription = createElement('p', {classNames: ['cardDescription']}, userObj.description || CARD_CONSTANTS.cardDescription);
const cardArticle = createElement('article', {classNames: ['cardContainer']}, imgWrapper, cardName, cardDescription);
const card = createElement('li', {classNames: ['userCardWrapper']}, cardArticle);
return card;
}
function createUserCardImageWrapper(userObj, fullName) {
const userImgElem = createElement('img', {
classNames: ['cardImg'],
attributes: {
src: userObj.profilePicture,
alt: fullName,
'data-id': userObj.id,
},
listeners: {
error: errorHandler,
load: loadHandler,
},
});
const initialsElem = createElement(
'div',
{ classNames: ['initials'] },
getInitials(fullName)
);
const imgWrapperElem = createElement(
'div',
{
classNames: ['cardImgWrapper'],
attributes: { id: `imgWrapper${userObj.id}` },
},
initialsElem
);
return imgWrapperElem;
}
function errorHandler({ target }) {
target.remove();
}
function loadHandler({
target,
target: {
dataset: { id },
},
}) {
document.getElementById(`imgWrapper${id}`).append(target);
}
I associate social networks with icons in this way (on the example of Twitter):
const SUPPORTED_SOCIAL_NETWORKS = new Map([
[
"twitter.com",
{
src: "https://w7.pngwing.com/pngs/872/50/png-transparent-computer-icons-social-media-logo-twitter-social-media-blue-logo-social-media-thumbnail.png",
alt: "twitter link for",
},
],
["www.facebook.com"],
]);
This is actually quite straightforward, see the comments in the code
// Get the network hosts for of a user and optionally filter them
let getNetworkHosts = (userId, filter) => {
if (match = users.filter( user => userId === user.id )) {
contacts = match[0].contacts.map( url => (new URL(url)).hostname)
if (filter) contacts = contacts.filter(filter)
return contacts
}
}
// A filter that can be applied to a list
let supportedNetworkFilter = elem => SUPPORTED_SOCIAL_NETWORKS[elem]
// Create an image tag
let createImage = (url) => {
let img = document.createElement("img")
img.src = url
return img
}
users =
[ { "id": 7
, "firstName": "Daniel"
, "lastName": "Day-Lewis"
, "profilePicture": "https://i2.wp.com/comicbookdebate.com/wp-content/uploads/2019/07/There20will20be20blood202-750x460.jpg?resize=750%2C460&ssl=1"
, "contacts":
[ "https://www.facebook.com/JasonStatham/"
, "https://twitter.com/realjstatham"
, "https://www.instagram.com/jasonstatham/?hl=ru"
]
}
]
const SUPPORTED_SOCIAL_NETWORKS =
{ "twitter.com":
{ src: "https://w7.pngwing.com/pngs/872/50/png-transparent-computer-icons-social-media-logo-twitter-social-media-blue-logo-social-media-thumbnail.png"
, alt: "twitter link for"
}
, "www.facebook.com": {} // Oops, facebook has no face
}
// Add some images to a div
let div = document.getElementById('images')
for (network of getNetworkHosts(7, supportedNetworkFilter)) {
div.appendChild(createImage(SUPPORTED_SOCIAL_NETWORKS[network].src))
}
<div id="images"></div>

Recursive function only returning first result

I'm trying to implement a file upload feature using native DnD and the browser API.
I have to functions like so:
export function buildFileTree(allObjects) {
let fileTree = {}
allObjects.forEach((item, index) => {
traverseFileTree(fileTree, null, item)
})
return fileTree
}
export function traverseFileTree(fileTree, parent = null, item) {
let parentId = !!parent ? fileTree[parent].id : parent
if (item.isFile) {
item.file(file => {
fileTree[item.fullPath] = {
id: uuidv4(),
name: item.name,
parent_id: parentId,
file: item
}
})
}
if (item.isDirectory) {
fileTree[item.fullPath] = {
id: uuidv4(),
name: item.name,
is_directory: true,
parent_id: parentId,
file: null
}
let dirReader = item.createReader()
dirReader.readEntries(entries => {
entries.forEach((entry, index) => {
traverseFileTree(fileTree, item.fullPath, entry)
})
})
}
}
Which I use like so, and I'm getting very weird results, when I console.log the object, it shows the correct data, but when I try to stringify it, access any other attributes, or iterate over it; it's only showing the first result.
const handleUploadDrop = (files) => {
const finalFiles = buildFileTree(files)
console.log(finalFiles) // This shows the correct data
console.log(JSON.stringify(finalFiles)) // This only shows 1 item!!!
console.log(Object.keys(finalFiles).length) // This prints 1
}
I'm very confused by this and any help would be greatly appreciated.

MBDReact: How do I make a <datatable> row clickable?

I have setup my table in admin side of our application with MDBReact using the datatable. This table shows some small details of the stories that I have.
Now I have to make a row clickable i.e. add onClick to make a function call with the story id passed as an argument to this function.
Question:
How do I add onClick event to the datatable row?
(Below is my code.)
class Posts extends Component {
componentDidMount() {
this.getPosts();
}
getPosts = async () => {
const response = await fetch("http://****************/get_posts");
const post_items = await response.json();
this.setState({ posts: post_items.result }, () => {
console.log(this.state.posts);
this.setState({ tableRows: this.assemblePost() });
});
};
assemblePost = () => {
let posts = this.state.posts.map((post) => {
let mongoDate = post.dateAdded.toString();
let mainDate = JSON.stringify(new Date(mongoDate));
return {
postTitle: post.storyTitle,
// postDescription: post.storyDescription,
dateAdded: mainDate.slice(1, 11),
thankedBy: post.thankedBy.length,
reportedBy: post.reportedBy ? post.reportedBy.length : "",
userEmail: post.userEmail[0],
categoryName: post.categoryName[0],
};
});
console.log(posts);
return posts;
};
state = {
posts: [],
tableRows: [],
};
render() {
const data = {
columns: [
{
label: "Story Title",
field: "postTitle",
},
{ label: "Category Name", field: "categoryName" },
{
label: "User Email",
field: "userEmail",
},
{
label: "Date Added",
field: "dateAdded",
},
{
label: "Thanked",
field: "thankedBy",
},
{
label: "Reported",
field: "reportedBy",
},
],
rows: this.state.tableRows,
};
return (
<div className="MDBtable">
<p className="posts">Posts List</p>
<MDBDataTable striped bordered hover data={data} />
</div>
);
}
}
export default Posts;
To pull this off, here's what I did, but you'll need to appreciate these:
MDBDataTable requires you to manually define the columns and rows.
For data to render seamlessly, you define columns.field that correspond to rows[key]
Now, here's the logic, if you define a rows[key] that does not correspond to any columns.field, then that rows[key] is defined for an entire row just like we often pass index when working with map().
So based on the above observations,you can just pass the click event as a key/value pair to the row.And it will work just fine.
// ...
assemblePost = () => {
let posts = this.state.posts.map(
(post, i) => {
let mongoDate = post.dateAdded.toString();
let mainDate = JSON.stringify(new Date(mongoDate));
return {
index: i + 1, // advisable to pass a unique identifier per item/row
clickEvent: () => this.handleClick(storyId), // pass it a callback function
postTitle: post.storyTitle,
// ...others
categoryName: post.categoryName[0],
};
});
console.log(posts);
return posts;
};
// ...
Notice this clickEvent: () => this.handleClick(storyId), will be attached to the entire row.

Edit function not saving changes to state data in React

I am trying to provide functionality in my webpage for editing state data.
Here is the state structure
state = {
eventList:[
{
name: "Coachella"
list: [
{
id: 1,
name: "Eminem"
type: "rap"
}
{
id: 2,
name: "Kendrick Lamar"
type: "rap"
}
]
}
]
}
I want to be able to edit the list arrays specifically the id, name, and type properties but my function doesn't seem to edit them? I currently pass data I want to override id name and type with in variable eventData and an id value specifying which row is selected in the table which outputs the state data.
Here is the function code:
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList;
eventListNew.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
When I run the code the function doesn't alter the single map variable and I can't seem to pinpoint the reason why. Any help would be great
edit:
Implementing Captain Mhmdrz_A's solution
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
I get a new error saying Cannot read property list of undefined in another file that uses the map function to render the state data to the table?
This is the part of the other file causing the error:
render() {
const EventsList = this.props.eventList.map((event) => {
return event.list.map((single) => {
return (
map() return a new array every time, but you are not assigning it to anything;
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.forEach((single) => {
if (single.id == id) {
single = eventData;
}
});
return event
});
this.setState({
eventList: eventListNew,
});
};
const editPickEvent = (eventData, id) => {
const updatedEventList = this.state.eventList.map(event => {
const updatedList = event.list.map(single => {
if (single.id === id) {
return eventData;
}
return single;
});
return {...event, list: updatedList};
});
this.setState({
eventList: updatedEventList,
});
}
Example Link: https://codesandbox.io/s/crazy-lake-2q6ez
Note: You may need to add more checks in between for handling cases when values could be null or undefined.
Also, it would be good if you can add something similar to the original data source or an example link.
Turns out primitive values are pass by value in javascript, which I didn't know and why the assignment wasn't working in some of the previous suggested answers. Here is the code that got it working for me:
editEvent = (EventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
const newList = event.list.map((single) => {
return single.id == id ? EventData : single;
});
return { ...event, list: newList };
});
this.setState({
eventList: eventListNew,
});
};

How can I set a userId inside the users object when an icon is clicked

I have a mock userId which should be saved inside the users object of the reactions object when a certain icon is clicked inside my react component.
Below is a function updateUploadReaction that is supposed to do that for me. The logic is this, when an icon is clicked and this particular userId does not exist in the users object, it sets it inside the user object and adds 1, on clicking again it sets it to false and subtracts 1. So far, this is what I have, but it simply keeps subtracting 3 each time I click. I need a guide on exactly how to do that.
Here's a link to the full App. updateUploadReaction is inside components/home/reducers.js
reaction object
{
reactions: {
dislike: {
count: 0,
users: {},
},
like: {
count: 0,
users: {},
},
maybe: {
count: 0,
users: {},
},
},
}
function
function updateUploadReaction(id, type, uploads) {
const updatedUploads = new Map([...uploads.entries()]);
const upload = updatedUploads.get(id);
const userId = uuid();
uploads.forEach(() => {
if (!userId {
upload.reactions[type].count += 1;
upload.reactions[type]..users[userId] = true;
} else {
upload.reactions[type].count -= 1;
upload.reactions[type].users[userId] = false;
}
});
updatedUploads.set(id, upload);
return updatedUploads;
}
I think you might be looking for something like this, I'm not sure if you want to add a new userId and remove it, or do something else. Perhaps it is an array of userIds? I think this might help you get on the right track though:
const upload1 = {
reactions: {
dislike: {
count: 0,
users: { userId: 1 },
},
},
}
const upload2 = {
reactions: {
dislike: {
count: 0,
users: {},
},
},
}
const uploads = [ upload1, upload2 ];
const updateObjectReaction = ( id, type, uploads ) => {
uploads.forEach( upload => {
const { users } = upload.reactions[ type ]
if ( Object.values( users ).includes( id ) ) {
delete users.userId
}
else {
users.userId = id;
}
} );
console.log( { upload1Users: uploads[ 0 ].reactions.dislike.users } )
console.log( { upload2Users: uploads[ 1 ].reactions.dislike.users } )
}
updateObjectReaction( 1, "dislike", uploads )

Categories

Resources