I am trying to merge two objects that I am getting from 2 different api calls(the example here is just a sample). How can I merge the UserId array of object and the userCredentials array together in the user state? I want the state to look like this user:[{id: 1, name"john", country="de"},{id: 2, name"micheal", country="us"}]
...
import React from "react";
import "./styles.css";
export default class App extends React.Component {
constructor() {
super();
this.state = {
user: []
};
}
componentDidMount() {
//api call 1 receiving user Id and name
const UserId = [{ id: 1, name: "john" }, { id: 2, name: "micheal" }];
this.setState({ user: UserId });
//api call 2 receiving userCredentials
const userCredentials = [
{ id: 1, country: "de" },
{ id: 1, country: "us" }
];
this.setState({
user: { ...this.state.user, credentials: userCredentials }
});
}
render() {
console.log("values", this.state);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
</div>
);
}
}
...
my sample code is
https://codesandbox.io/s/fancy-water-5lzs1?file=/src/App.js:0-754
Basically you need to map thru 1 array and find if each object in the array exists in another array and use spread operator and return the merged object in map callback
Working demo
Use the code below:
// option 1 - if you know the keys of the object
let merged = UserId.map(u => {
const user = userCredentials.find(uc => uc.id === u.id);
if (user) u["country"] = user.country;
return u;
});
// option 2 - generic merge
let merged2 = UserId.map(u => {
const user = userCredentials.find(uc => uc.id === u.id);
if (user) return { ...u, ...user };
return u;
});
You could use 'array.concat([])' to merge two array objects together. See bellow example.
let UserId = [{ id: 1, name: "john" }, { id: 2, name: "micheal" }];
const userCredentials = [{ id: 1, country: "de" },{ id: 1, country: "us" }];
const newArray = UserId.concat(userCredentials);
Since you have defined UserId as a const you cannot change it. So you have to make it to let or var to modify the variable.
Related
I am working in react and have a resonse ( ReviewerService.getReviewers()) that returns an array of values:
0: {id: 1, firstName: 'John', lastName: 'Doe', email: 'johndoe#aol.com', responses: '{"q1":"yes","q2":"no","q3":"yes","rating":4}'}
1: {id: 2, firstName: 'bob', lastName: 'jefferson', email: 'bob#aol.com', responses: '{"q1":"bob","q2":"yes","q3":"yes","rating":5}'}.
If this.state = { reviewers: [] }.
How do i pass the response data into reviewers and parse the responses property at the same time? Therefore, then I can access these properties of the responses easily.
class ListReviewsComponent extends Component {
constructor(props) {
super(props);
this.state = {
reviewers: [],
};
}
async componentDidMount() {
await ReviewerService.getReviewers().then((res) => {
this.setState({ reviewers: res.data });
});
this.setState({ reviewers.responses: JSON.parse(this.state.reviewers.responses)}); // error
}
can this work
async componentDidMount() {
try {
const res = await ReviewerService.getReviewers();
// got the data
const reviewers = res.data;
// not parse the responses for each reviewer
const mappedReviewers = reviewers?.map(reviewer => {
try {
const parsedResponses = JSON.parse(reviewer.responses)
// do you need to convert parsedResponses to an array ??
return {
...reviewer,
responses: parsedResponses
}
} catch(error) {
return {
...reviewer,
responses: [] //
}
}
});
this.setState({ reviewers: mappedReviewers})
} catch (error) {
// log errors
}
}
Hope this helps you to sort out the issue
I think that you array returned by
ReviewerService.getReviewers()
should be in json format, treated or parsed, before, setted in setState:
data = [ {id: 1, firstName: 'John', lastName: 'Doe', email: 'johndoe#aol.com', responses: '{"q1":"yes","q2":"no","q3":"yes","rating":4}'}
,{id: 2, firstName: 'bob', lastName: 'jefferson', email: 'bob#aol.com', responses: '{"q1":"bob","q2":"yes","q3":"yes","rating":5}'} ];
Then you do this, putting array in a json treated object format
async componentDidMount() {
await ReviewerService.getReviewers().then((res) => {
this.setState({ reviewers: res.data });
});
When you do:
this.setState({ reviewers: res.data });
You area putting on this.state.reviewers, all list and all objects nested in.
You could access this.state on this component like this method below:
getResponsesOfReviewersOnArrayByIndex = (index) => {
return this.state.reviewers[index].responses
}
Or just in some method access,
this.state.reviewers[i].firstName
You can try this to understand better the JSON parse function:
const reviewersData = '[{"name":"neymar"}, {"name":"junior"}]';
const reviewers = JSON.parse(reviewersData);
console.log(reviewers[0].name);
In this W3Schools to see more examples of JSON.parse()
Hope this helps.
I'm trying to create new object with different properties name from Array.
Array is:
profiles: Array(1)
0:
column:
name: "profileName"
title: "Profile name"
status: "Active"
I want to create new function that return object with two properties:
id: 'profileName',
profileStatus: 'Active'
The function that I have create is returning only one property as undefined undefined=undefined.
function getProfile(profiles) {
if (!profiles.length) return undefined;
return profiles.reduce((obj, profile) => {
console.log('profiles', profile);
return ({
...obj,
id: profile.column.name,
profileStatus: profile.status,
});
}, {});
}
The function getProfile is taking as input array 'profiles' from outside,
I've just tested here and this seems to be working actually
const getProfile1 = (p) => p.reduce((obj, profile) =>({
...obj,
id: profile.column.name,
profileStatus: profile.status,
}), {});
You can use map as an alternative.
var profiles = [{"column":{"name": "profileName3","title": "3Profile name"},"status": "Active"},{"column":{"name": "profileName","title": "Profile name"},"status": "Active"}];
function getProfile(profiles) {
if (!profiles.length) return undefined;
return profiles.map(function(profile,v){
return {id:profile.column.name,profileStatus: profile.status};
});
}
console.log(getProfile(profiles));
Whenever I use reduce in this way, I usually index the final object by some sort of an id. As noted in another answer, you could use map in this situation as well. If you really want your final data structure to be an object, however, you could do something like this:
/**
* returns object indexed by profile id
*/
const formatProfiles = (profiles) => {
return profiles.reduce((obj, profile) => {
return {
...obj,
[profile.id]: {
id: profile.column.name,
profileStatus: profile.status,
}
};
}, {});
};
const profiles = [
{
id: 0,
status: 'active',
column: {
name: "profile_name_1",
title: "profile_title_1",
},
},
{
id: 1,
status: 'inactive',
column: {
name: "profile_name_2",
title: "profile_title_2",
}
}
];
const result = formatProfiles(profiles);
/**
* Result would look like this:
*/
// {
// '0': { id: 'profile_name_1', profileStatus: 'active' },
// '1': { id: 'profile_name_2', profileStatus: 'inactive' }
// }
I'm having two data arrays which are coming from API and sample arrays would be like this
Array 1
[
{userId: 1
description: "Student"
imagePath: "test.png"
status: 1
}]
Array 2
[
{id: 85
accountName: "Rahul"
accountNumber: "11145678"
}
]
In my reactnative app view there's search bar and user should be able to search from these two arrays. So I merged these two arrays into one using
this.searchArray =this.filterArray[0].concat(this.filterArray[1])
So, my searchArray is a single array with Array1 and Array2 data. sample below
[
{userId: 1
description: "Student"
imagePath: "test.png"
status: 1
},
{id: 85
accountName: "Rahul"
accountNumber: "11145678"
}]
My search function is below (I need to search from account number or description)
//Search Filter
searchFilter =searchText=>{
const searchTextData = searchText.toUpperCase();
const userSearch = this.searchArray.filter(item => {
const itemData = `${item.description && item.description.toUpperCase()} ${item. accountName && item.accountName.toUpperCase()}`;
return itemData.indexOf(searchTextData) > -1;
});
}
The search functionality is not working with accountName. It's not getting any results. But if I remove ${item. accountName && item.accountName.toUpperCase()} , then it's working showing data with description. But I need to filter from both
In your array one object can have description or accountNumber so do a check if that exists include it in the itemData variable.
Try doing this
searchFilter =searchText=>{
const searchTextData = searchText.toUpperCase();
const userSearch = this.searchArray.filter(item => {
const itemData = `${item.hasOwnProperty('description'))?item.description.toUpperCase():''} ${item.hasOwnProperty('accountNumber')?item.accountNumber:''}`;
return itemData.indexOf(searchTextData) > -1;
});
}
First merge the two objects into one:
Object.keys(arr2[0]).forEach(key => {
arr1[0][key] = arr2[0][key]
})
Then create the search function:
function searchObject(obj, value){
return Object.keys(obj).some(key => {
return obj[key] === value
})
}
let arr1=[{userId:1,description:"Student",imagePath:"test.png",status:1}],arr2=[{id:85,accountName:"Rahul",accountNumber:"11145678"}];
Object.keys(arr2[0]).forEach(key => {
arr1[0][key] = arr2[0][key]
})
function searchObject(obj, prop, value){
return obj[prop] === value
}
console.log(searchObject(arr1[0], "accountName", "asdf"))
console.log(searchObject(arr1[0], "accountName", "Rahul"))
I am trying to move an object from one array to another. Think of it like adding / moving a friend from non-friend to friend. I have two arrays, which can be seen below, and I am trying to move an object (i.e. a friend) from possible to current via it's 'id'. In the below example, I am trying to move Parker from possible to current with id = 2.
state = {
current: [
{
id: 1,
name: 'peter'
}
],
possible: [
{
id: 2,
name: 'parker'
}
]
}
function addFriend(state, action) {
const { current, possible } = state;
const addedFriend = Object.assign(
{},
state.possible.splice(action.payload.index, 1)
);
current.push(addedFriend);
const newState = { current, possible };
return newState;
}
Since you can remove multiple elements with splice(), it returns an array. Index the result to get the specific object. You don't need to use Object.assign(), that just copies the value (which just converts the array into an object whose properties are the array indexes).
var state = {
current: [
{
id: 1,
name: 'peter'
}
],
possible: [
{
id: 2,
name: 'parker'
}
]
};
function addFriend(state, action) {
const { current, possible } = state;
const addedFriend = state.possible.splice(action.payload.index, 1)[0];
current.push(addedFriend);
const newState = { current, possible };
return newState;
}
state = addFriend(state, {payload: { index: 0 }});
console.log(state);
I'm not sure why you're returning a new state object, since you're modifying the old state in place.
It is not that time-efficient if you want a fast running code. But it follows immutability.
We just ignore the item from possible, and add it to current.
state = {
current: [
{
id: 1,
name: 'peter'
}
],
possible: [
{
id: 2,
name: 'parker'
}
]
}
function addFriend(state, action) {
const { current, possible } = state;
return {
...state,
current: current.concat(possible[action.payload.index]),
possible: possible.filter((_, index) => index !== action.payload.index)
}
}
state = addFriend(state, {payload: {index: 0}})
console.log(state)
I’ve got a very deeply nested object in my React state. The aim is to change a value from a child node. The path to what node should be updated is already solved, and I use helper variables to access this path within my setState.
Anyway, I really struggle to do setState within this nested beast. I abstracted this problem in a codepen:
https://codesandbox.io/s/dazzling-villani-ddci9
In this example I want to change the child’s changed property of the child having the id def1234.
As mentioned the path is given: Fixed Path values: Members, skills and variable Path values: Unique Key 1 (coming from const currentGroupKey and both Array position in the data coming from const path
This is my state object:
constructor(props) {
super(props);
this.state = {
group:
{
"Unique Key 1": {
"Members": [
{
"name": "Jack",
"id": "1234",
"skills": [
{
"name": "programming",
"id": "13371234",
"changed": "2019-08-28T19:25:46+02:00"
},
{
"name": "writing",
"id": "abc1234",
"changed": "2019-08-28T19:25:46+02:00"
}
]
},
{
"name": "Black",
"id": "5678",
"skills": [
{
"name": "programming",
"id": "14771234",
"changed": "2019-08-28T19:25:46+02:00"
},
{
"name": "writing",
"id": "def1234",
"changed": "2019-08-28T19:25:46+02:00"
}
]
}
]
}
}
};
}
handleClick = () => {
const currentGroupKey = 'Unique Key 1';
const path = [1, 1];
// full path: [currentGroupKey, 'Members', path[0], 'skills', path[1]]
// result in: { name: "writing", id: "def1234", changed: "2019-08-28T19:25:46+02:00" }
// so far my approach (not working) also its objects only should be [] for arrays
this.setState(prevState => ({
group: {
...prevState.group,
[currentGroupKey]: {
...prevState.group[currentGroupKey],
Members: {
...prevState.group[currentGroupKey].Members,
[path[0]]: {
...prevState.group[currentGroupKey].Members[path[0]],
skills: {
...prevState.group[currentGroupKey].Members[path[0]].skills,
[path[1]]: {
...prevState.group[currentGroupKey].Members[path[0]].skills[
path[1]
],
changed: 'just now',
},
},
},
},
},
},
}));
};
render() {
return (
<div>
<p>{this.state.group}</p>
<button onClick={this.handleClick}>Change Time</button>
</div>
);
}
I would appreciate any help. I’m in struggle for 2 days already :/
Before using new dependencies and having to learn them you could write a helper function to deal with updating deeply nested values.
I use the following helper:
//helper to safely get properties
// get({hi},['hi','doesNotExist'],defaultValue)
const get = (object, path, defaultValue) => {
const recur = (object, path) => {
if (object === undefined) {
return defaultValue;
}
if (path.length === 0) {
return object;
}
return recur(object[path[0]], path.slice(1));
};
return recur(object, path);
};
//returns new state passing get(state,statePath) to modifier
const reduceStatePath = (
state,
statePath,
modifier
) => {
const recur = (result, path) => {
const key = path[0];
if (path.length === 0) {
return modifier(get(state, statePath));
}
return Array.isArray(result)
? result.map((item, index) =>
index === Number(key)
? recur(item, path.slice(1))
: item
)
: {
...result,
[key]: recur(result[key], path.slice(1)),
};
};
const newState = recur(state, statePath);
return get(state, statePath) === get(newState, statePath)
? state
: newState;
};
//use example
const state = {
one: [
{ two: 22 },
{
three: {
four: 22,
},
},
],
};
const newState = reduceStatePath(
state,
//pass state.one[1],three.four to modifier function
['one', 1, 'three', 'four'],
//gets state.one[1].three.four and sets it in the
//new state with the return value
i => i + 1 // add one to state.one[0].three.four
);
console.log('new state', newState.one[1].three.four);
console.log('old state', state.one[1].three.four);
console.log(
'other keys are same:',
state.one[0] === newState.one[0]
);
If you need to update a deeply nested property inside of your state, you could use something like the set function from lodash, for example:
import set from 'lodash/set'
// ...
handleClick = () => {
const currentGroupKey = 'Unique Key';
const path = [1, 1];
let nextState = {...this.state}
// as rightly pointed by #HMR in the comments,
// use an array instead of string interpolation
// for a safer approach
set(
nextState,
["group", currentGroupKey, "Members", path[0], "skills", path[1], "changed"],
"just now"
);
this.setState(nextState)
}
This does the trick, but since set mutates the original object, make sure to make a copy with the object spread technique.
Also, in your CodeSandbox example, you set the group property inside of your state to a string. Make sure you take that JSON string and construct a proper JavaScript object with it so that you can use it in your state.
constructor(props) {
super(props)
this.setState = { group: JSON.parse(myState) }
}
Here's a working example:
CodeSandbox