Map function is not setting value to each element - javascript

I'm using map function to set values to the array of objects. but whenever i pass multiple objects it only stores only last one. I can't find why it is doing this.
const [tracks, setTracks] = useState([]);
const [playerTracks, setPlayerTracks] = useState([])
const playTracks = () => {
setPlayerTracks(
{ id: 8, track_name: "Bad Guy", url: "001.mp3", artwork: "001.jpg", artist: "Ed Sheeran" },
{ id: 9, track_name: "Bury A Friend", url: "002.mp3" , artwork: "002.jpg", artist: "Taylor Swift"}
)
playerTracks.map(track => {
setTracks([{
title: track.track_name,
artist: track.artist_name,
audioSrc: track.url,
image: track.artwork
}])
})
console.log(playerTracks) // both objects
console.log(tracks) // only last object
}

Instead of updating state in map, you need to create new object and then store into state like below:-
const [tracks, setTracks] = useState([]);
const [playerTracks, setPlayerTracks] = useState([])
const playTracks = () => {
setPlayerTracks(
{ id: 8, track_name: "Bad Guy", url: "001.mp3", artwork: "001.jpg", artist: "Ed Sheeran" },
{ id: 9, track_name: "Bury A Friend", url: "002.mp3" , artwork: "002.jpg", artist: "Taylor Swift"}
)
const newTracks = playerTracks.map(track => {
return {
title: track.track_name,
artist: track.artist_name,
audioSrc: track.url,
image: track.artwork
};
})
setTracks(newTracks);
}

Related

How to dynamically grab object keys and use the keys as part of a method?

I would like to create the filter function to dynamically search through each key for the user search results. How can I do this without hardcoding each key.
const data = [
{ title: "title1", body: "body1", footer: "footer1" },
{ title: "title2", body: "body2", footer: "footer2" },
{ title: "title3", body: "body3", footer: "footer3" },
];
const search = 'footer1'
const filter = data.filter(
(item) => item.title.includes(search) || item.body.includes(search) || item.footer.includes(search)
);
For each object that you loop over in the .filter() callback, you can grab all of its values using Object.values(), then you can use .some() on this array to check if any of the values within this array contain the search string:
const data = [ { title: "title1", body: "body1", footer: "footer1" }, { title: "title2", body: "body2", footer: "footer2" }, { title: "title3", body: "body3", footer: "footer3" }, ];
const search = 'footer1';
const filter = data.filter(
(item) => Object.values(item).some(val => val.includes(search))
);
console.log(filter);
You can use a for...in loop to access each prop on the item object.
const data = [
{ title: "title1", body: "body1", footer: "footer1" },
{ title: "title2", body: "body2", footer: "footer2" },
{ title: "title3", body: "body3", footer: "footer3" },
];
const search = 'footer1'
const filter = data.filter(
(item) => {
for(const prop in item){
if(item[prop].includes(search)){
return true
}
}
return false
});
console.log(filter)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
You can iterate all the keys(using Object.keys) inside the filter and check the value for each key.
const data = [
{ title: "title1", body: "body1", footer: "footer1" },
{ title: "title2", body: "body2", footer: "footer2" },
{ title: "title3", body: "body3", footer: "footer3" },
];
const search = "filter1";
const filter = data.filter((item) => {
for (key of Object.keys(item)) {
if (item[key].includes(search)) {
return item[key].includes(search);
}
}
});
console.log(filter);

Javascript - transforming an object of array list to new formated one?

I'm trying to transform an object contain array to another one with javascript. Below is an example of the object field and what the formatted one should look like.
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
I need The new Fields to looks like this
let newFields = {
name: 'GAME',
tags:[
{ name: 'playPES', value: "{{PES}}" },
{ name: 'playFIFA', value: "{{FIFA}}" }
]},
One contributor suggested me a method like this but i think something need to modify in it but couldn't figure it out.
export const transform = (fields) => ({
tags: Object .entries (fields) .map (([name, innerFields]) => ({
name,
tags: innerFields.map(({code, title: title: {en})=>({name: en, value: code}))
}))
});
// newFields= transform(Fields)
I'm new working with javascript so any help is greatly appreciated, Thanks.
const transform = (o) => {
return Object.entries(o).map((e)=>({
name: e[0],
tags: e[1].map((k)=>({name: (k.title)?k.title.en:undefined, value: k.code}))
}))[0]
}
console.log(transform({
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
}))
Using the entries method you posted:
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
// 1. Obtain keys and values from first object
Fields = Object.entries(oldFields);
// 2. Create new object
const newFields = {};
// 3. Create the name key value pair from new Fields array
newFields.name = Fields[0][0];
// 4. Create the tags key value pair by mapping the subarray in the new Fields array
newFields.tags = Fields[0][1].map(entry => ({ name: entry.title.en, value: entry.code }));
Object.entries(Fields) will return this:
[
"GAME",
[TagsArray]
]
And Object.entries(Fields).map will be mapping this values.
The first map, will receive only GAME, and not an array.
Change the code to something like this:
export const transform = (Fields) => {
const [name, tags] = Object.entries(Fields);
return {
name,
tags: tags.map(({ code, title }) => ({
name: title.en,
value: code
}))
}
}
Hope it help :)
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
let newFields = {
name: 'GAME',
tags:[
{ name: 'playPES', value: "{{PES}}" },
{ name: 'playFIFA', value: "{{FIFA}}" }
]
}
let answer = {
name: "Game",
tags: [
]
}
Fields.GAME.map(i => {
var JSON = {
"name": i.title.en,
"value": i.code
}
answer.tags.push(JSON);
});
console.log(answer);
I think that this is more readable, but not easier... If you want the result as object you need to use reduce, because when you do this
Object.keys(Fields)
Your object transform to array, but reduce can change array to object back.
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
const result = Object.keys(Fields).reduce((acc, rec) => {
return {
name: rec,
tags: Fields[rec].map(el => {
return {
name: el.title.en,
value: el.code
}
})
}
}, {})
console.log(result)
let Fields = {
GAME: [
{ code: '{{PES}}', title: { en: "playPES"} },
{ code: '{{FIFA}}', title: { en: "playFIFA " } },
]
};
const transform = (fields) => ({
tags: Object .entries (fields) .map (([name, innerFields]) => ({
name,
tags: innerFields.map(({code, title: title,en})=>({name: title.en, value: code}))
}))
});
//check required output in console
console.log(transform(Fields));

Counting occurrence of values in an array of objects in another array of objects

I need to create a Graphql query that outputs data from two arrays of objects. The arrays are:
const authors = [
{
name: 'Robert Martin',
id: 'afa51ab0-344d-11e9-a414-719c6709cf3e',
born: 1952
},
{
name: 'Martin Fowler',
id: 'afa5b6f0-344d-11e9-a414-719c6709cf3e',
born: 1963
},
{
name: 'Fyodor Dostoevsky',
id: 'afa5b6f1-344d-11e9-a414-719c6709cf3e',
born: 1821
},
{
name: 'Joshua Kerievsky', // birthyear not known
id: 'afa5b6f2-344d-11e9-a414-719c6709cf3e'
},
{
name: 'Sandi Metz', // birthyear not known
id: 'afa5b6f3-344d-11e9-a414-719c6709cf3e'
}
];
And:
const books = [
{
title: 'Clean Code',
published: 2008,
author: 'Robert Martin',
id: 'afa5b6f4-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Agile software development',
published: 2002,
author: 'Robert Martin',
id: 'afa5b6f5-344d-11e9-a414-719c6709cf3e',
genres: ['agile', 'patterns', 'design']
},
{
title: 'Refactoring, edition 2',
published: 2018,
author: 'Martin Fowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring, edition 3',
published: 2018,
author: 'Martin Fowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring, edition 4',
published: 2018,
author: 'Martin Cowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring to patterns',
published: 2008,
author: 'Joshua Kerievsky',
id: 'afa5de01-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring', 'patterns']
},
{
title: 'Practical Object-Oriented Design, An Agile Primer Using
Ruby',
published: 2012,
author: 'Sandi Metz',
id: 'afa5de02-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring', 'design']
},
{
title: 'Crime and punishment',
published: 1866,
author: 'Fyodor Dostoevsky',
id: 'afa5de03-344d-11e9-a414-719c6709cf3e',
genres: ['classic', 'crime']
},
{
title: 'The Demon ',
published: 1872,
author: 'Fyodor Dostoevsky',
id: 'afa5de04-344d-11e9-a414-719c6709cf3e',
genres: ['classic', 'revolution']
}
];
The desired output format for a query like this:
query {
allAuthors {
name
bookCount
}
}
is like so:
"data": {
"allAuthors": [
{
"name": "Robert Martin",
"bookCount": 2
},
{
"name": "Martin Fowler",
"bookCount": 1
},
{
"name": "Fyodor Dostoevsky",
"bookCount": 2
},
{
"name": "Joshua Kerievsky",
"bookCount": 1
},
{
"name": "Sandi Metz",
"bookCount": 1
}
]
}
I've found a way to count the amount of books for each author and output the data in the desired format (a good example of that here: Summarize count of occurrences in an array of objects with Array#reduce). However this approach ignores other fields in the data, such as "born" and "genres". If I was to expand the query like so:
query {
allAuthors {
name
bookCount
born
}
}
It wouldn't output anything for the field "born". What would be the smart way to create the query resolver? Spread operator? Reduce?
* EDIT *
My unnecessarily complicated solution for counting the books here:
const newBooks = books.reduce((acc, cv) => {
const arr = acc.filter(obj => {
return obj.author === cv.author;
});
if (arr.length === 0) {
acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
} else {
arr[0].bookCount += 1;
}
return acc;
}, []);
const array = [];
books.forEach(book => {
const object = {
name: book.author
};
array.push(object);
return array;
});
const unique = array.map(a => a.name);
result = {};
for (var i = 0; i < unique.length; ++i) {
if (!result[unique[i]]) result[unique[i]] = 0;
++result[unique[i]];
}
const entries = Object.entries(result);
const finalAnswer = [];
entries.forEach(entry => {
const object = {
name: entry[0],
bookCount: entry[1]
};
finalAnswer.push(object);
return finalAnswer;
});
console.log(finalAnswer);
You could map the authors and use filter to get the bookCount for each author
const authors=[{name:'Robert Martin',id:'afa51ab0-344d-11e9-a414-719c6709cf3e',born:1952},{name:'Martin Fowler',id:'afa5b6f0-344d-11e9-a414-719c6709cf3e',born:1963},{name:'Fyodor Dostoevsky',id:'afa5b6f1-344d-11e9-a414-719c6709cf3e',born:1821},{name:'Joshua Kerievsky',id:'afa5b6f2-344d-11e9-a414-719c6709cf3e'},{name:'Sandi Metz',id:'afa5b6f3-344d-11e9-a414-719c6709cf3e'}],
books=[{title:'Clean Code',published:2008,author:'Robert Martin',id:'afa5b6f4-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Agile software development',published:2002,author:'Robert Martin',id:'afa5b6f5-344d-11e9-a414-719c6709cf3e',genres:['agile','patterns','design']},{title:'Refactoring, edition 2',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 3',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 4',published:2018,author:'Martin Cowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring to patterns',published:2008,author:'Joshua Kerievsky',id:'afa5de01-344d-11e9-a414-719c6709cf3e',genres:['refactoring','patterns']},{title:'Practical Object-Oriented Design, An Agile Primer Using Ruby ',published:2012,author:'Sandi Metz',id:'afa5de02-344d-11e9-a414-719c6709cf3e',genres:['refactoring','design']},{title:'Crime and punishment',published:1866,author:'Fyodor Dostoevsky',id:'afa5de03-344d-11e9-a414-719c6709cf3e',genres:['classic','crime']},{title:'The Demon ',published:1872,author:'Fyodor Dostoevsky',id:'afa5de04-344d-11e9-a414-719c6709cf3e',genres:['classic','revolution']}];
const output = authors.map(({ born, name }) => {
const bookCount = books.filter(b => b.author === name).length;
return { name, born, bookCount }
})
console.log(output)
I think you can add a statement to your reducer function to add the desired fields. I added the single line, and annotated the rest of the method so you can see what's going on:
const newBooks = books.reduce((acc, cv) => {
// acc is an "accumulation" of the results so far.
// cv is the next item that hasn't been processed.
// Search for author in "accumulator" array acc. Put results in arr.
const arr = acc.filter(obj => {
return obj.author === cv.author;
});
if (arr.length === 0) {
// Haven't seen this author, yet. Add new item to "accumulator" array.
acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
} else {
// This author already exists in "accumulator" array, referenced by arr[0].
// Update pre-existing item.
arr[0].bookCount += 1;
arr[0].born = cv.born; // <-- This is the new code that is required.
}
return acc;
}, []);

How to map properties of an object to another?

I know there are many questions on this matter, but I can't figure out how to apply it in my case.
Code is as following:
const mailDocumentSchema = new schema.Entity('mailDocuments', {}, { idAttribute: 'identifier' });
const mailSchema = new schema.Entity('mails', { emailDocument: [mailDocumentSchema] }, { idAttribute: 'identifier' });
const mailAccountSchema = new schema.Entity('mailAccounts', { mails: [mailSchema] }, { idAttribute: 'address' });
const mailMapper = (item: any): Mail => ({
id: item.identifier,
title: item.subject,
documents: item.emailDocument,
subject: item.realSubject,
receiver: item.receiver,
createdDate: item.createdDate,
sendDate: item.sendDate,
body: item.body
});
const mailDocumentMapper = (item: any): MailDocument => ({
id: item.identifier,
docId: item.oldDocId,
name: item.name,
createdDate: item.createdDate,
bodyStatus: item.bodyStatus
});
export const undefinedDocumentsMapper = (response: any[]): NormalizedMailbox => {
const undefinedDocuments = map(groupBy(response, item => item.receiver), (item, key) => ({ name: null, address: key, mails: item }));
const normalizedResponse = normalize(undefinedDocuments, [mailAccountSchema]);
return {
mailAccounts: Object.values<MailAccount>(normalizedResponse.entities.mailAccounts),
mails: Object.values<any>(normalizedResponse.entities.mails).map(mailMapper),
mailDocuments: Object.values<any>(normalizedResponse.entities.mailDocuments).map(mailDocumentMapper)
};
};
API response is:
[
{ identifier: "...", title: "...", receiver: "...", emailDocuments: [{...}] },
...
]
What I want is to map the receiver and title properties from mails entity objects to mailDocuments entity objects.
As a result to get mailDocuments entity objects as such:
{
id: "...",
docId: "...",
name: "...",
createdDate: "...",
bodyStatus: ...,
title: "...",
receiver: "..."
}
How to accomplish this?
If I understand correctly, you can do the following:
mails.forEach(mail => {
mail.documents.forEach(document => {
document.receiver = email.receiver
document.title = email.title
})
})

How do I create a master/root schema with normalizr 2?

Say I have the following schemas and data:
const user = new schema.Entity('users')
const video = new schema.Entity('videos', {
user
})
const comment = new schema.Entity('comments', {
video
})
// Sample Data
const users = [
{ id: 1, object: "User", name: "User 1" },
{ id: 2, object: "User", name: "User 2" },
]
const videos = [
{ id: 1, object: "Video", user: users[0] },
{ id: 2, object: "Video", user: users[1] },
]
const comments = [
{ id: 1, object: "Comment", video: videos[0] },
{ id: 2, object: "Comment", video: videos[0] },
]
How would I setup a resource and resourceList schema to wrap all my schemas so that in my api calls I would only ever have to reference resource or resourceList eg:
api.videos.list().then(
// response.data is an array containing multiple videos
response => normalize(response.data, resourceList)
)
// entities: { videos: [...], users: [...] }
or
api.videos.retrieve(videoId).then(
// response.data is an object containing a single video
response => normalize(response.data, resource)
)
I've tried doing this:
const resource = {
user,
video,
}
const resourceList = new schema.Array(resource, inferSchema)
function inferSchema({ object: objectName = '' }) {
return objectName.charAt(0).toLowerCase() + objectName.slice(1)
}
But that didn't seem to work.

Categories

Resources