The original data (old) looks like this:
Object {
"pageParams": Array [
undefined,
],
"pages": Array [
Object {
"cursor": 0,
"items": Array [
Object {
"content": "This is a users post!",
"createdAt": "2022-09-03T02:37:10.287Z",
"id": "93d13314-630e-4948-94a4-f75677afa7ba",
"likeCount": 10,
"likedByUser": false,
...
Just need to toggle likedByUser on click. passing in (id,performerId) in flatlist onclick.
Trying to map like this but I don't believe it is returning the original structure for the infinite scroll. need to return old with one changed object.
const likeHandler = (id: string, performerId: string) => {
const LikePostMutation: LikePostInput = {
id: id,
performerProfileId: performerId,
}
mutateLikes.mutateAsync(LikePostMutation).then(() => {
context.setQueryData(['fan.performer.getPostsFeed', {}], (old) => {
if (!old) return old
return old.pages.map((item: { items: any[] }) =>{
item?.items.map((item) => {
if (item?.id === id) {
let newItem = {
...item,
likedByUser: !item.likedByUser,
}
return { newItem }
}
}),
}
)
})
})
}
The error message TypeError: undefined is not an object (evaluating 'o[Symbol.iterator]') is from flatlist. I think the error message coming from view:
This is the shape of logged data after mapping incorrectly: I need it to be in the pages array and in an outer object.
}
Object {
content": "This is a users post!",
"createdAt": "2022-09-03T02:37:10.287Z",
"id": "93d13314-630e-4948-94a4-f75677afa7ba",
"likeCount": 10,
"likedByUser": true,
...
open to any suggestions on how to get better at this.
when doing immutable updates to arrays, you need to map over each item and return the same structure. infinite query structure is nested so it gets a bit boilerplat-y, but this should work:
context.setQueryData(['fan.performer.getPostsFeed', {}], (old) => {
if (!old) return old
return {
...old,
pages: old.pages.map(page) => ({
...page,
items: page.items.map((item) => {
if (item?.id === id) {
return {
...item,
likedByUser: !item.likedByUser,
}
return item
}
})
})
}
})
if you don't like the deep spreading, take a look at immer
Related
I have a below JSON,
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
And am trying to add new data inside the nested array i.e., "vpainfo". I have tried using the below code and able to adding the new values inside "vpainfo".
var newdata = {"vpa":"first#bda","mccCode":"1111"};
var newObj =
Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>(todoInfo.accountNo=="50190000")?[
...todoInfo.vpainfo,
newdata
]: todoInfo)
});
And the resulted object is,
{"todos":[[{"vpa":"log#bda","mccCode":"0000"},{"vpa":"first#bda","mccCode":"1111"}]]}
But few of the key and values(accountNo and name) are getting missed, how do we get the full object with the latest updated values?
You only return the array, not the actual object, hence the error.
var original = {
"todos": [
{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [
{
"vpa": "log#bda",
"mccCode": "0000"
}
]
}
]
}
const newdata = {"vpa":"first#bda","mccCode":"1111"};
const newObj = Object.assign({}, original,
{
todos: original.todos.map(todoInfo=>{
if(todoInfo.accountNo=="50190000"){
return {
...todoInfo,
vpainfo: [...todoInfo.vpainfo, newdata]
}
}
return todoInfo
})
});
console.log(newObj)
All those spread operators seem a little excessive...
If all you wanna do is add newdata to that existing array, then do that:
var original = {
"todos": [{
"accountNo": "50190000",
"name": "Sarkar",
"vpainfo": [{
"vpa": "log#bda",
"mccCode": "0000"
}]
}]
};
const newdata = {
"vpa": "first#bda",
"mccCode": "1111"
};
// Find the correct account.
const account = original.todos.filter(t => t.accountNo === '50190000')[0];
if (account) {
account.vpainfo.push(newdata);
}
console.log(original);
I don't really know how to express what I want, but I'll try.
So, I have an object with an array inside with the name of recipes, that I receive from my API, and a valuePath which is an object:
Object
{
recipes: [
{
test: {
items: [
{
type: 'test1',
}
]
}
}
]
}
ValuePath
{
"allRecipes": {
"array": "recipes",
"values": {
"allTypes": {
"array": "test",
"values": {
"type": "type"
}
}
}
}
}
Briefly what I have to do, is iterate over the array recipes through out the valuePath, dynamically, because the array and the values can change. I don't really know how to explain it better and how to iterate thought deeply nested objects/array's having a valuePath as a reference to find the values.
What I've tried so far...
export const test = (object, valuePath) => {
for (const prop in valuePath) {
object = object[valuePath[prop].array]; // find the array
if (Array.isArray(object)) {
object.forEach(objRef => {
console.log('valueRef', objRef);
});
}
console.log('props->', valuePath[prop].values); // find the values
}
};
I think i need a recursion, but have no clue how to do one.
If I understood your problem, this could be an implementation...
If you run it with your data and path, it will return test1.
// INPUTS
const data = {
recipes: [
{
test: {
items: [
{
type: 'test1',
}
]
}
}
]
}
const path = {
"allRecipes": {
"array": "recipes",
"values": {
"allTypes": {
"array": "test",
"values": {
"type": "type"
}
}
}
}
}
// this is just an helper method for arrays...
Array.prototype.first = function () { return this[0] }
// this is an helper function that tells us whether
// a path object is still traversable.
// from what I understood, if it contains an `array` property
// we should follow it...
const isTraversable = path => !!path.array
// this is the actual implementation of the algorithm
const traverse = (data, path) => {
const nextPath = Object.values(path).first()
if ( isTraversable(nextPath) ) {
const array = data[nextPath.array]
// I noticed that at a certain point in the data object,
// we need to traverse an array, and in another it is an
// object with an `items` property.
// this next lines helps determine how go down
const nextData = Array.isArray(array) ? array.first() : array.items
// we recurse on the traversed data and path
return traverse(nextData, nextPath.values)
}
return data.first()[path.type]
}
console.log(traverse(data, path))
Please try this, I hope it will help you..
let obj = {
recipes: [
{
test: {
items: [
{
type: 'test1',
},
],
},
},
],
};
obj.recipes.forEach(test => {
test.test.items.forEach(items => {
console.log(items.type);
});
});
So,
I am receiving the data that has the following information:
{
"data":[
{
"vote_count":22222,
"id":299537,
"ready":false,
},
{
"vote_count":2850,
"id":299534,
"ready":true,
},
]
}
Now I need to make a new object that would contain the same structure but with some properties, ie:
{
"data": [
{
"ready":false,
},
{
"ready":true,
}
]
}
I need the solution that is scalable, imagine having a set of data with 50 properties for example. Also, I did find solutions with objects, but never with array of objects.
Thanks guys, I've been busting my head for three hours now.
You could use destrcuturing and shorthand property names to create new objects like this:
const input={"data":[{"vote_count":22222,"id":299537,"ready":false,},{"vote_count":2850,"id":299534,"ready":true,},]}
const data = input.data.map(({ ready }) => ({ ready }))
console.log({ data })
If you want to get a bunch of properties, you could create an array of properties you need. Then use Object.assign() or reduce to create a subset of each object like this:
const input={"data":[{"vote_count":22222,"id":299537,"ready":false,},{"vote_count":2850,"id":299534,"ready":true,},]}
const properties = ["vote_count", "ready"]
const data = input.data.map(a =>
Object.assign({}, ...properties.map(p => ({ [p]: a[p] })))
)
/* You could also use reduce like this:
input.data.map(a => properties.reduce((r, p) => ({ ...r, [p]: a[p] }), {}))
*/
console.log({ data })
Map the properties you want
var obj1 = {
"data":[
{
"vote_count":22222,
"id":299537,
"ready":false,
},
{
"vote_count":2850,
"id":299534,
"ready":true,
},
]
}
var obj2 = {}
obj2.date = obj1.data.map(data => ({ ready: data.ready}));
console.log(obj2)
You can do it using Array#map method and Array#reduce method
const input = {
"data": [{
"vote_count": 22222,
"id": 299537,
"ready": false,
},
{
"vote_count": 2850,
"id": 299534,
"ready": true,
},
]
}
const extract = ['ready']
const data = input.data.map(o => extract.reduce((obj, k) => (obj[k] = o[k], obj), {}))
console.log({ data })
This question already has answers here:
JavaScript "cannot read property "bar" of undefined [duplicate]
(4 answers)
Closed 4 years ago.
I'm trying to change JSON structure for push to my database
my old JSON :
[
{"name": "nameGallery"},
{"img": "1.jpg"},
{"img": "2.img"}
]
And I want to group "img" variable to "Images" Array like this:
[
{
"name": "nameGallery",
"Images": [
{"img": "1.jpg"},
{"img": "2.img"}
]
}
]
I'm trying to use object.assign to manage it but I don't know why it error.
function getData() {
fetch('text/upload.json').then((res) => res.json())
.then((data) => {
console.log(data);
data = data.map(o => Object.assign({}, o,
{ Images: o.Images.map(({ img }) => ({ img: img })) }
));
})
}
My Result:
In your solution you are calling .map which will create you one array entry for every array entry in your initial data.
As you described, you expect one object as a result and not an array of object. So look at the following :
const data = [{
name: 'nameGallery',
},
{
img: '1.jpg',
},
{
img: '2.img',
},
];
// You want to create a new object using all the others objects
const ret = data.reduce((tmp, x) => {
// If the key below is 'img', we push the object into 'Images'
// void 0 means undefined
if (x.img !== void 0) {
tmp.Images.push(x);
return tmp;
}
// If the key is not 'img'
// We copy the keys into tmp
return {
...tmp,
...x,
};
}, {
// initialize 'Images' key here it won't be undefined
// when pushing the first data
Images: [],
});
console.log(ret);
You can try something like this:
function getData() {
fetch('text/upload.json').then((res) => res.json())
.then((data) => {
const name = data.find(o => !!o.name);
return {
name: name.name,
Images: data.filter(o => !!o.img)
};
})
}
I am retrieving a document from PouchDB in an Angular Service. The document is retrieved in the following format:
{
"_id":"segments",
"_rev":"1-4f0ed65cde23fe724db13bea1ae3bb13",
"segments":[
{ "name":"Aerospace" },
{ "name":"Auto repair" },
{ "name":"Commercial" },
{ "name":"Education" },
{ "name":"Energy" },
{ "name":"Farm/ranch" },
{ "name":"Furniture" },
{ "name":"Heavy Equipment" },
{ "name":"Hobbyist" },
{ "name":"Infrastructure" },
{ "name":"Luxury/Leisure" },
{ "name":"Military" },
{ "name":"MUP" },
{ "name":"Processing" },
{ "name":"Rail" },
{ "name":"Transportation" }
]}
And I want to map that to a new Array that would look like:
[
{ value: "Aerospace", viewValue: "Aerospace" },
{ value: "Auto Repair", viewValue: "Auto Repair" },
{ value: "Commercial", viewValue: "Commercial" }
...
]
To accomplish this, I have tried this code in my Service:
getSegments(): Observable<any[]> {
return from(this.database.get('segments'))
.pipe(
map((results) => results.segments)
);
}
And I transform the array in my Component like this:
segments: SegmentsLookup[] = [];
...
this.lookupDbService.getSegments()
.subscribe(data => {
data.forEach(element => {
this.segments.push({value: element.name, viewValue: element.name});
});
});
This works but I know there is a way to map this properly back in the Service code. Also, when done this way, the compiler complains about the "results.segments" stating "Property "segments" does not exist on type '{}'.
How do I map the data retrieved to the Array that I need in the Service's "getSegments" method?
You can do the transformation is 2 steps:
pipe/map to extract the segments
array/map to convert to the final data type
Please see an example here: https://stackblitz.com/edit/angular-ikb2eg?file=src%2Fapp%2Fapp.component.ts
let transformedData = observableData.pipe(
map(data => {
console.log(data, data.segments.length);
return data.segments.map(element => {
return { value: element["name"], viewValue: element["name"] };
});
})
);
transformedData.subscribe(data => {
this.mylist = data;
});
You can use the RxJS operator flatMap, which is a alias of mergeMap.
The official documentation describes flatMap as follows:
Projects each element of an observable sequence to an observable
sequence and merges the resulting observable sequences or Promises or
array/iterable into one observable sequence.
We can use flatMap and then the RxJS operator map like this:
flatMap to extract the segments
map to convert to the final data type
const transformedData = observableData.pipe(
flatMap(data => data.segments),
map(segment => ({
value: segment['name'],
viewValue: segment['name'],
})),
)
transformedData.subscribe(data => {
this.mylist = data;
});
Detailed explanation of how this works:
This article nicely explains how flatMap works and implements a version of flatMap, which works with arrays rather than RxJS observables, as follows:
function flatMap (arr, fn) {
return arr.reduce((flatArr, subArray) => flatArr.concat(fn(subArray)), [])
}
If we use this with the result of your database query we'll see we extract the segments.
const data = {
"_id": "segments",
"_rev": "1-4f0ed65cde23fe724db13bea1ae3bb13",
"segments": [
{ "name": "Aerospace" },
{ "name": "Auto repair" },
// ...
]};
const segments = flatMap([data], x => x.segments);
console.log(segments);
// > [
// > { "name": "Aerospace" },
// > { "name": "Auto repair" },
// > ...
// > ]
The RxJS flatMap operator returns an observable stream of segments, rather than an array of segments.
You can extend the map function and remove it from the component as follows :
map(result => {
result = result.segments;
let data = [];
result.forEach(element => {
data.push({value: element.name, viewValue: element.name});
});
return data;
});