Not able to set state as array of objects inside map function - javascript

I am getting data from word press API then I structure the data inside the map function. I am not able to set state as array of objects inside map function I
get Arrays of different objects or Objects of different objects but not a single
Array of Objects
const rsp = response.data;
const map = rsp && rsp.map(item => {
let struct = {};
let data = [];
const id = item.id;
const title = item.title['rendered'];
const image = item['_embedded']['wp:featuredmedia'][0].source_url;
const banner = item.ACF.banner_image.url;
const products = item.ACF.celebrity_products;
let store={
id:id,
title:title,
image:image,
banner:banner,
products:products
};
setRes(prevState => {
// struct['data'] =store;
// data.push(store);
return{ ...prevState,
id: store.id,
name: store.title,
uri: store.image,
banner: store.banner,
products: store.products
}})
});

I think you'd want more something like :
const [items, setItems] = useState([])
rsp && setItems(rsp.map(item => ({
id: item.id,
name: item.title['rendered'],
uri: item['_embedded']['wp:featuredmedia'][0].source_url,
banner: item.ACF.banner_image.url,
products: item.ACF.celebrity_products
})))

I think you didn't provide the complete story of you code. but I think we can help.
if you intend to have the result of your processing as an array inside the map variable then you should've returned something inside it like
let oddNumber = [ 1, 3, 5 ];
const map = oddNumber.map( oneOddNumber => { return oneOddNumber * 2; } );
// now map is [ 2, 6, 10 ]
or if you intend to save the array you want using setRes function then you should've done something like
setRes(prevState => ({
...prevState,
theResultArray: [...prevState.theResultArray, { id: store.id, ..... } ]
}))
and now you state have the property name theResultArray which is an array of object which you've iterated on.
hope you can provide more complete code so help can be more clear next time :D

Related

JavaScript - Creating Object with specific format out of 2 Arrays

I have 2 Arrays.
The Array "people" just holds basic information of some people.
The Array "subscriptions" has a lot of different subscriptions to games.
I want to have have an Array, where I can sort of have an Overview of what game subscriptions each person has. This is not real code I use, I am just trying to get used to JavaScript.
an example Element of an Array called people:
{"number":4251,"name":"Alex","surname":"Scott"}
an example Element of an Array called subscriptions:
{"number":4329,game:"Tetris"}
I want to make a new new Array with the following format:
person: (people[i]), subscriptions: [(subscriptions[j], subscriptions[j+k], ...)]
What I tried:
const array3 = people.map(x => {"person": x , "subscriptions": subscriptions.filter(y => y.number === x.number)});
It get this Error:
SyntaxError: Unexpected token :
How can I insert multiple key value pairs in in these Objects?
This happens because your argument in the map is interperting the { as opening bracket of the method body.
const arr = [{some: 1, object: 2}, {some:3, object:4}]
arr.map((o) => {original: o, cool: 'yes'})
To resolve this you have to explicitly return the object or add additional parenthesis to let the interperter know that this is not the method body:
const arr = [{some: 1, object: 2}, {some:3, object:4}]
const mapped = arr.map((o) => {return {original: o, cool: 'yes'}})
console.log(mapped)
const arr = [{some: 1, object: 2}, {some:3, object:4}]
const mapped = arr.map((o) => ({original: o, cool: 'yes'}))
console.log(mapped)
I hope this helps!
In your original code there is a syntax error, when it comes to the mapping part. Either you go with the long version of this command or, since you directly want to return the element, you can use the short version:
const people = [{"number":4251,"name":"Alex","surname":"Scott"}, {"number": 4329, "name":"Mr", "surname": "Tetri"}]
const subscriptions = [{"number":4329,game:"Tetris"}, {"number":4329, game:"Solitaire"}, {number: 4251, game: "Tetris"}]
// using an anonymous function including full body
const arrayLongVersion = people.map(x => { return {"person": x , "subscriptions": subscriptions.filter(y => y.number === x.number)} });
// using an anonymous function with direct return of elements
const arrayShortVersion = people.map(x => ({person: x , subscriptions: subscriptions.filter(y => y.number === x.number)}))
Here you go
const people = [
{"number":1,"name":"Alex","surname":"Scott"},
{"number":2,"name":"John","surname":"Anderson"},
{"number":3,"name":"Jacob","surname":"Sanderson"},
{"number":4,"name":"Lionel","surname":"Messi"},
{"number":5,"name":"Cristiano","surname":"Ronaldo"}
];
const subscriptions = [
{"number":1,game:"Tetris"},
{"number":2,game:"Super Mario"},
{"number":3,game:"Fortnite"},
{"number":4,game:"FIFA"},
{"number":5,game:"Zelda"}
];
const peopleSubscriptions = people.map(person => {
return {
...person,
subscriptions: subscriptions.filter(sub => sub.number === person.number)
}
});
console.log(peopleSubscriptions);

Optimal way of transforming flat map to nested data structure using React.js?

I've been pondering the best way to handle grouping in my app. It's a video editing app and I am introducing the ability to group layers. If you're familiar with Figma or any design/video editing program then there is usually the ability to group layers.
To keep this simple in the app the video data is a map
const map = {
"123": {
uid: "123",
top: 25,
type: "text"
},
"345": {
uid: "345",
top: 5,
type: "image"
},
"567": {
uid: "567",
top: 25,
type: "group"
children: ["345", "123"]
}
}
Then I am grouping them inside a render function (this feels expensive)
const SomeComponent = () => {
const objects = useMemo(() => makeTrackObjects(map), [map]);
return (
<div>
{objects.map(object => {
return <div>Some layer that will change the data causing re-renders</div>
})}
</div>
)
}
Here is the function that does the grouping
const makeTrackObjects = (map) => {
// converts map to array
const objects = Object.keys(map).map((key: string) => ({ ...map[key] }));
// flat array of all objects to be grouped by their key/id
const objectsInGroup = objects
.filter((object) => object.type === "group")
.map((object) => object.children)
.flat();
// filter out objects that are nested/grouped
const filtered = objects.filter((object) => !objectsInGroup.includes(object.uid))
// insert objects as children during render
const grouped = filtered.map((object) => {
const children = object.children
? {
children: object.children
.map((o, i) => {
return {
...map[o]
};
})
.flat()
}
: {};
return {
...object,
...children
};
});
// the core data is flat but now nested for the UI. Is this inefficient?
return grouped
}
Ideally I would like to keep the data flat, I have a lot of code that I would have to update to go deep in the data. It feels nice to have it flat and transformers in certain areas where needed.
The main question is does this make sense, is it efficient, and if not then why?
If you are running into performance issues, one area you may want to investigate is how you are chaining array functions (map, filter, flat, etc). Each call to one of these functions creates an intermediate collection based on the array it receives. (For instance, if we chained 2 map functions, this is looping through the full array twice). You could increase performance by creating one loop and adding items into a collection. (Here's an article that touches on this being a motivation for transducers.)
I haven't encountered a performance issue with this before, but you may also want to remove spread (...) when unnecessary.
Here is my take on those adjustments on makeTrackObjects.
Update
I also noticed that you are using includes while iterating through an array. This is effectively O(n^2) time complexity because each item will be scanned against the full array. One way to mitigate is to instead use a Set to check if that content already exists, turning this into O(n) time complexity.
const map = {
"123": {
uid: "123",
top: 25,
type: "text"
},
"345": {
uid: "345",
top: 5,
type: "image"
},
"567": {
uid: "567",
top: 25,
type: "group",
children: ["345", "123"]
}
};
const makeTrackObjects = (map) => {
// converts map to array
const objects = Object.keys(map).map((key) => map[key]);
// set of all objects to be grouped by their key/id
const objectsInGroup = new Set();
objects.forEach(object => {
if (object.type === "group") {
object.children.forEach(child => objectsInGroup.add(child));
}
});
// filter out objects that are nested/grouped
const filtered = objects.filter((object) => !objectsInGroup.has(object.uid))
// insert objects as children during render
const grouped = filtered.map((object) => {
const children = {};
if (object.children) {
children.children = object.children.map(child => map[child]);
}
return {
...object,
...children
};
});
// the core data is flat but now nested for the UI. Is this inefficient?
return grouped
}
console.log(makeTrackObjects(map));

exclude already existing items from array in React

I am facing a challenge. I have 2 tables in firebase that I need to merge into 1 array. The only thing is that items are the same, they have to be removed from the array. So if an item already exists in an array, it should no longer be added to the array. So at the moment I get to see the items twice. Is there a possibility to prevent this? My function looks like this:
fetchItems(){
this.setState({
personalItems:[],
inactiveItems:[],
sprintItems:[],
})
let ref = Firebase.database().ref('/sprints/1/items/');
ref.on('value' , snapshot => {
snapshot.forEach((childSnap) => {
let state = childSnap.val();
console.log('firebase output:'+state)
var newelement = {title: state.title, author: state.author,user: state.user, public: state.public, x: state.x, y: state.y, key: state.key, match: state.match, notes: state.notes, status: state.status};
this.setState(prevState => ({
personalItems: [...prevState.personalItems, newelement],
}));
console.log('firebase'+state.name);
});
})
let refInactive = Firebase.database().ref('/users/'+this.state.user+'/items/');
refInactive.on('value' , snapshot => {
snapshot.forEach((childSnap) => {
let state = childSnap.val();
var newelement = {author: state.author, title: state.postit, status: state.status, key: state.key, user: state.user, match: state.match, x: state.x, y: state.y, public: state.public, notes:state.notes };
this.setState(prevState => ({
personalItems: [...prevState.personalItems, newelement],
}));
});
})
}
My database looks like this:
So you see that these items have the same key. These are also identical to each other. However, if 1 has already been added, this is sufficient, now they are both added.
Here is how you can merge an array into another without duplicates.
for(arr1Element of array1){
if(!array2.some(arr2Element => arr2Element.id === arr1Element.id)){
array2.push(arr1Element)
}
}
You could use a Set, which doesn't let you add duplicate values.
So the easiest you could achieve is something like:
const yourFinalArray = [...new Set([...arr1, ...arr2]);
Hope this helps.

How to map this type of API data?

I'm trying to map a data from API, but I have some problems with it. How can I push only names to array? There is no property called "name" or something like this.
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) => arr.push(data),
this.setState({
data: arr
})
);
}
{this.state.data.map((person) => {
return <li>{person}</li>
})}
Description
Okay, so having looked at the data returned from that URL, it returns an object, therefore, a simple way would be to use Object.keys. As you can see, I'm using the map function to produce a new array from the array produced by Object.keys, from there passing the array produced from the map function into the log function, just for a little demo.
I'm not saying this is the most efficient/performance friendly way ever, however it is a clean, simple, clear & concise way to do things.
const url = `https://contact-browser.herokuapp.com/contacts`;
const log = args => console.log(args);
fetch(url).then(r => r.json()).then(d => log(Object.keys(d).map(k => d[k])));
Edit
As mentioned in the comments, if you want to be more efficient and you don't care about the keys, you could just use Object.values, with this approach it's just less data to deal with, less processing, etc.
const url = `https://contact-browser.herokuapp.com/contacts`;
const log = args => console.log(args);
fetch(url).then(r => r.json()).then(d => log(Object.values(d)));
Looking at the data source it sort of looks like this:
{
"1": "Jason Brown",
"2": "Angela Houston",
"3": "Angela Maynard",
"4": "Natasha West",
"5": "Christina Lee",
"6": "Bryan Baker",
//other stuff
}
You can try the following:
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) =>
this.setState({
data: Object.values(data)
})
);
}
Note that Object.values, Object.entries, Object.keys, for ... in and the sorts are not guaranteed return data in any order so if you want to order by the key you can do the following:
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) =>
this.setState({
data: Object.entries(data)
.map(([key,val])=>[Number(key),val])//make key a number
.sort(([a],[b])=>a-b)//sort by ket
.map(([,val])=>val)//do not use the key for data
})
);
}
However; when you render your data array you need a unique key or React will give you a warning. To prevent this warning you can provide the object key with your data:
componentDidMount() {
let url = `https://contact-browser.herokuapp.com/contacts`;
let arr = []
fetch(url).then(resp => resp.json()).then((data) =>
this.setState({
data: Object.entries(data)
.map(([key,val])=>({key:Number(key),val}))//make key a number
.sort((a,b)=>a.key-b.key)//sort by key
})
);
}
{this.state.data.map(({key,val}) => {
return <li key={key}>{val}</li>
})}

Updating json with lodash

Actually I need to handle mysite frontend fully with json objects(React and lodash).
I am getting the initial data via an ajax call we say,
starred[] //returns empty array from server
and am adding new json when user clicks on star buton it,
starred.push({'id':10,'starred':1});
if the user clicks again the starred should be 0
current_star=_findWhere(starred,{'id':10});
_.set(curren_star,'starred',0);
but when doing console.log
console.log(starred); //returns
[object{'id':10,'starred':0}]
but actually when it is repeated the global json is not updating,while am performing some other operations the json is like,
console.log(starred); //returns
[object{'id':10,'starred':1}]
How to update the global , i want once i changed the json, it should be changed ever.Should I get any idea of suggesting some better frameworks to handle json much easier.
Thanks before!
Working with arrays is complicated and usually messy. Creating an index with an object is usually much easier. You could try a basic state manager like the following:
// This is your "global" store. Could be in a file called store.js
// lodash/fp not necessary but it's what I always use.
// https://github.com/lodash/lodash/wiki/FP-Guide
import { flow, get, set } from 'lodash/fp'
// Most basic store creator.
function createStore() {
let state = {}
return {
get: path => get(path, state),
set: (path, value) => { state = set(path, value, state) },
}
}
// Create a new store instance. Only once per "app".
export const store = createStore()
// STARRED STATE HANDLERS
// Send it an id and get back the path where starred objects will be placed.
// Objects keyed with numbers can get confusing. Creating a string key.
const starPath = id => ['starred', `s_${id}`]
// Send it an id and fieldId and return back path where object will be placed.
const starField = (id, field) => starPath(id).concat(field)
// import to other files as needed
// Add or replace a star entry.
export const addStar = item => store.set(starPath(item.id), item)
// Get a star entry by id.
export const getStar = flow(starPath, store.get)
// Get all stars. Could wrap in _.values() if you want an array returned.
export const getStars = () => store.get('starred')
// Unstar by id. Sets 'starred' field to 0.
export const unStar = id => store.set(starField(id, 'starred'), 0)
// This could be in a different file.
// import { addStar, getStar, getStars } from './store'
console.log('all stars before any entries added:', getStars()) // => undefined
const newItem = { id: 10, starred: 1 }
addStar(newItem)
const star10a = getStar(10)
console.log('return newItem:', newItem === star10a) // => exact match true
console.log('star 10 after unstar:', star10a) // => { id: 10, starred: 1 }
console.log('all stars after new:', getStars())
// Each request of getStar(10) will return same object until it is edited.
const star10b = getStar(10)
console.log('return same object:', star10a === star10b) // => exact match true
console.log('return same object:', newItem === star10b) // => exact match true
unStar(10)
const star10c = getStar(10)
console.log('new object after mutate:', newItem !== star10c) // => no match true
console.log('star 10 after unstar:', getStar(10)) // => { id: 10, starred: 0 }
console.log('all stars after unstar:', getStars())
I think the problem is in mutating original state.
Instead of making push, you need to do the following f.e.:
var state = {
starred: []
};
//perform push
var newItem = {id:10, starred:1};
state.starred = state.starred.concat(newItem);
console.log(state.starred);
//{ id: 10, starred: 1 }]
var newStarred = _.extend({}, state.starred);
var curr = _.findWhere(newStarred, {id: 10});
curr.starred = 0;
state = _.extend({}, state, {starred: newStarred});
console.log(state.starred)
//{ id: 10, starred: 0 }]
To solve this in a more nice looking fashion, you need to use either React's immutability helper, or ES6 stuff, like: {...state, {starred: []}} instead of extending new object every time. Or just use react-redux =)

Categories

Resources