Accessing object value inside array using forEach? [duplicate] - javascript

This question already has answers here:
How to find object in array by property in javascript?
(3 answers)
Closed 4 years ago.
Hi my first question here. I have an array of objects that looks like this:
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2},
{title: 'title3', id: 3}]
From this array I want to extract the dynamic value of title by id. Meaning if I run a function on this I'd like to receive 'title1' when id is specified as '1'. I've tried a number of things including a forEach loop that right now looks like this:
albums.forEach(function(key, index, arr) {
arr[index].title })
I read elsewhere on this site that for/forEach loops could be useful in extracting value from an object inside an array of objects. But I guess I don't fully understand how a forEach loop works in this regard, because I'm unable to extract any value from it. if inside the forEach loop I call a console.log (so likeconsole.log(arr[index].title) it nicely logs the value of the title property for each element in the array. But when I try to return (return arr[index].title) and call the function it comes up as undefined.
So in summary I have these two questions:
What is the best way to access a value inside an object inside an array of objects?
How does a forEach loop work exactly when trying to access an object inside an array of objects?
Thanks for any input, suggestions, feedback

Try this code:
const albums = [{ title: 'title1',id: 1}, {title: 'title2',id: 2},{title: 'title3',id: 3}]
var idToFind = 2;
albums.forEach((element) => {
if (element.id == idToFind) {
console.log(element.title)
}
})

(1) To find a value in an array, Array.prototype.find is almost always your best bet.
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2}, {title: 'title3', id: 3}]
const findAlbum = (id) => albums.find(album => album.id === id)
console.log(findAlbum(2)); //~> {title: 'title2', id: 2}
(2) forEach can be made to work, but it is not generally the best fit for this job. forEach is about running functions against elements. It does not intrinsically capture the results. So you could wrap it like this:
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2}, {title: 'title3', id: 3}]
const findAlbum = (id) => {
let found
albums.forEach(album => {
if (!found && album.id === id) {
found = album
}
})
return found
}
console.log(findAlbum(2)) //~> {title: 'title2', id: 2}
That code is clearly more complex. And it obscures the fact that what you want to do is to "find" something.
(It also has a more insidious problem. If you wanted to find something in a list of potentially false-y values, it will keep searching if the value is false-y. So if your were to alter this to try to find the first even number in [3, 5, 7, 0, 11, 14, 13, 17, 20], it would skip the false-y 0 and return 14.) You can fix this by splitting found into two variables, one to check whether we've found anything and another to store the found value, but that makes the code still more complex, especially when we have a find method available.)
(3) You could break this down into some reusable helpers. I won't write them here. But you could write generic reusabale where and equals functions so that the code looks more like this:
const findAlbum = (id) => albums.find(where('id', equals(id)))
That looks a lot more readable to me.

JavaScript has several ways to extract and manipulate objects in arrays. Here are some of the most common that pertain to your problem:
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2},
{title: 'title3', id: 3}];
// For each is for doing something that causes side effects
albums.forEach(({title}) => console.log(title))
// nothing is returned
// console output:
// title1
// title2
// title3
// Map is for transforming each object in the array into something else
const titles = albums.map(({title}) => title);
// => ['title1', 'title2', title3']
// If you need to find a specific one, use find
const albumWithId3 = albums.find(({id}) => id === 3);
// => {title: 'title3', id: 3}
// If you need to find a subset, use filter
const ablumsWithIdsLessThan3 = albums.filter(({id}) => id < 3)
// => [{title: 'title1', id: 1}, {title: 'title2', id:2}]
To directly answer your question, you probably want something like this:
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2},
{title: 'title3', id: 3}];
const getTitle = album => album ? album.title : undefined;
const idToAlbum = (myId, albums) => albums.find(({id}) => myId === id);
const idToTitle = (myId, albums) => getTitle(idToAlbum(myId, albums));
const title = idToTitle(1, albums);
console.log(title);
// => 'title1'
Or, if you prefer an imperative style:
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2},
{title: 'title3', id: 3}];
const idToTitle = (id, albums) => {
for (const album of albums) {
if (album.id === id) {
return album.title;
}
}
}
const title = idToTitle(1, albums);
console.log(title);
// => 'title1'

Try this:
function getTitleById(id)
{
var i;
for (i = 0; i < albums.length; i++) {
if(albums[i]['id'] == id)
return albums[i]['title'];
}
}

If you know the position of the object in the array, just access it with the position. I.e console.log(albums[4].title). But if you don't know the position you need to go over each and search for the object in question. Then the best way would be to write a search function that returns the object.
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2},
{title: 'title3', id: 3}];
console.log(serachArrayByTitle(albums,'title2'));
function serachArrayByTitle(arrayToSerach,titleToSearchFor){
for(var i=0;i<arrayToSerach.length;i++){
if(arrayToSerach[i].title===titleToSearchFor){
return arrayToSerach[i];
}
}
}
forEach() executes the given callback function for each element of the array. Read more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

For consistency, you should use forEach scoped, like this:
var Album = function(arr){
this.arr = arr;
this.get = function(id){
this.arr.forEach((element) => {
if (element.id == id) {
console.log(element.title)
}
})
}
}
a = new Album([{title: 'title1', id: 2}]);
a.get(2)
this should return 'title1'

You can use Array#find() to look for an array element that matches whatever criteria you want.
Then if there is a match return whatever you want from that element
const albums = [{title: 'title1', id: 1}, {title: 'title2', id: 2},
{title: 'title3', id: 3}]
const getTitleById = (id) => {
const o = albums.find(album => album.id === id);
return o && o.title
}
console.log(getTitleById(2))
console.log(getTitleById(3))

Related

Adding multiple object attributes to the same array?

I have an object from which I'd like to take some attributes and put them into a brand new array. I can do that for one attributes, but I can't do with multiple. I want to take specific attributes (title and year in this case) and put their values in the new array. It should look like this: const movies1 = ["AAA", "BBB"]. I'm not sure if I can do this with map or I have to do something completely different in order to achieve what I had in mind.
const test = [{
Title: 'AAAA',
Year: 'BBB',
Runtime:'100min',
Director:'CCC'
}]
const movies1 = test.map(({Title}) => Title, ({Year}) => Year)
console.log(movies1);
You can use flatMap to achieve this. flatMap is like map and then flat.
const test = [{
Title: 'AAAA',
Year: 'BBB',
Runtime:'100min',
Director:'CCC'
}]
const movies1 = test.flatMap(t => [t.Title, t.Year]);
console.log(movies1);
const test = [{
Title: 'AAAA',
Year: 'BBB',
Runtime:'100min',
Director:'CCC'
}]
const movies1 = test.map(({Title,Year}) => [Title,Year])
console.log(movies1);

JavaScript Get Indexes based from the Selected Array of Object Id

Today, I'm trying to get the list of javascript index based from the selected data id that I have.
I'm following this guide from https://buefy.org/documentation/table/#checkable where it needs something like this: checkedRows: [data[1], data[3]] to able to check the specific row in the table.
What I need to do is to check the table based from my web API response.
I have this sample response data.
response.data.checkedRows // value is [{id: 1234}, {id: 83412}]
and I have the sample data from the table.
const data = [{
id: 1234,
name: 'Jojo'
},{
id: 43221,
name: 'Jeff'
},{
id: 83412,
name: 'Kacey'
}]
So basically, I need to have something, dynamically, like this: checkedRows: [data[0], data[2]] because it matches the data from the response.data.checkedRows
So far, I tried using forEach
let selectedIndex = [];
response.data.checkedRows.forEach((d) => {
this.data.forEach((e) => {
if (d.id=== e.id) {
// need the result to be dynamic depending on the response.data.checkedRows
this.checkedRows = [data[0], data[2]]
}
});
});
I'm stuck here because I'm not sure how can I get the index that matches the selected checkedRows from response.
Any help?
Map the response checkedRows, and in the callback, .find the matching object in the array:
const checkedRows = [{id: 1234}, {id: 83412}];
const data = [{
id: 1234,
name: 'Jojo'
},{
id: 43221,
name: 'Jeff'
},{
id: 83412,
name: 'Kacey'
}];
const objs = checkedRows.map(({ id }) => (
data.find(obj => obj.id === id)
));
console.log(objs);
If there are a lot of elements, you can use a Set of the IDs to find instead to decrease the computational complexity:
const checkedRows = [{id: 1234}, {id: 83412}];
const data = [{
id: 1234,
name: 'Jojo'
},{
id: 43221,
name: 'Jeff'
},{
id: 83412,
name: 'Kacey'
}];
const ids = new Set(checkedRows.map(({ id }) => id));
const objs = data.filter(obj => ids.has(obj.id));
console.log(objs);

Add an object with setState() to an array of objects

My component's state is an array of objects:
this.state = {
userFavorites: [{id: 1, title: 'A'}, {id: 2, title: 'B'}]
}
I need to, everytime I click in a button, update my state with another object like the ones I have in my state above; for example: {id: 3, title: 'C'}.
If I simply concat them and set the state, the last object keeps being changed by the one that's being added.
Do I need the spread operator here?
You should do it this way. Below is the most recommended way to push values or objects to an array in react
this.setState( prevState => ({
userFavorites: [...prevState.userFavourites, {id: 3, title: 'C'}]
}));
To push to the beginning of the array do it this way
this.setState( prevState => ({
userFavorites: [{id: 3, title: 'C'}, ...prevState.userFavourites]
}));
for functional component
const [userFavorites, setUserFavorites] = useState([]);
setUserFavorites((prev) => [...prev, {id: 3, title: 'C'}])
There are multiple ways to do this. The simplest way for what you are needing to do is to use the spread operator. Note that item is the object being added to userFavorites.
this.setState({
userFavorites: [ ...this.state.userFavorites, item ],
});
Note: If at some point down the line you are needing to update a specific item in the array, you could use the update function from the immutability-helpers library. https://reactjs.org/docs/update.html
const userFavorites = [...this.state.userFavorites, {id: 3, title: 'C'}]
this.setState({ userFavorites })

Applying a property to each element in several arrays, then returning a flat map with one array

I have a collection that looks like this
[
{
count: 123,
description: 'some description',
articles: [
{...}
]
},
{
count: 234,
description: 'some description',
articles: [
{...}
]
}
]
Each object in the collection has a collection of articles. What I need is to apply the description to each article object in the respective collection in each element of the primary collection. I also want to end up with a flat array containing only articles. Clearly I'm using mergeMap incorrectly, but I'm not sure how to do it.
I have tried this
json$.pipe(
// Filter out empty arrays
filter(section => section.count > 0),
// Apply the descriptions
map(section => section.articles.map(a => (Object.assign(a, section.sectionName)))),
mergeMap(x => x.articles)
).subscribe(x => console.log(x));
But the articles do not have the description property in them, and it's not a flat array of articles. I've tried a few things but I'm unsure how to proceed
You only need to concatMap the outer observable, after adjusting each article.
const { Observable } = Rx;
const { map, concatMap, filter } = Rx.operators;
const json$ = Observable.from([
{
count: 123,
description: 'some description 123',
articles: [
{id: 1},
{id: 2},
]
},
{
count: 234,
description: 'some description 234',
articles: [
{id: 3},
{id: 4},
]
}
]);
const withDescription$ = json$.pipe(
filter(({count}) => count > 0),
concatMap(
({articles, description}) => Observable.from(articles).map(a => ({...a, description}))
)
);
withDescription$.subscribe(console.log);
<script src="https://unpkg.com/#reactivex/rxjs#^5/dist/global/Rx.min.js"></script>
If you don't need any special behavior on the inner observable, you could simplify to:
const withDescription$ = json$.pipe(
filter(({count}) => count > 0),
concatMap(
({articles, description}) => articles.map(a => ({...a, description}))
),
);

How do you create Object of Arrays in Javascript

I spent more time on this than I would like to admit. I have trouble constructing an object filled with an array.
I would like my data to look like this:
items={
{
'2012-05-22': [{text: 'item 1 - any js object'}],
'2012-05-23': [{text: 'item 2 - any js object'}],
'2012-05-24': [],
'2012-05-25': [{text: 'item 3 - any js object'},{text: 'any js object'}],
}
}
I am making a database call and the data I receive looks like this:
Object {start: "08:00:00", end: "09:00:00", full_name: "Tomomi", date: "2017-06-08", Barber_id: "1"…}
The data I am interested in is the full_name value and the date value.
This is what I have attempted:
let newItems = {};
axios.post(endpoint, {lookup: day.dateString}).then((customerData) => {
customerData.data.forEach((val,key)=>{
newItems = {[val.date]:[]};
newItems[val.date].push({name:val.full_name});
console.log(newItems);
})
}
It looks like this:
Object {2017-06-08: Array(1)}
2017-06-08
:
Array(1)
This is very close, but the problem is that my code is overwriting my data.
I am trying to create this dynamically:
'2012-05-25': [{text: 'item 3 - any js object'},{text: 'any js object'}],
So that each date can have many users. Hopefully, this makes sense.
Thanks for any help.
The function expression you pass to forEach has this as the first line:
newItems = {[val.date]:[]};
This resets the newItems object to an object with one date:name pair. You really want something more like:
newItems[val.date]?newItems[val.date].push({name:val.full_name}):newItems[val.date]=[];
var byDate = {}; // Object to store received data by-date
function addIntoByDate( obj ) {
byDate[obj.date] = byDate[obj.date] || [];
byDate[obj.date].push( obj );
}
// Simulate adding server data one by one
addIntoByDate( {date: "2017-06-08", full_name: "Cat", text:"Foo!!"} ); // < SAME DATE
addIntoByDate( {date: "2016-05-23", full_name: "Dog", text:"Bar"} );
addIntoByDate( {date: "2017-06-08", full_name: "Bug", text:"Baz..."} ); // < SAME DATE
// test
console.dir(byDate);
You can use object destructuring, computed property and Object.assign()
const newItems = {};
const data = [
{
start: "08:00:00"
, end: "09:00:00"
, full_name: "Tomomi"
, date: "2017-06-08"
, Barber_id: "1"
}
];
data.forEach(({date, full_name}) =>
Object.assign(newItems, {[date]: [{/* text: */ full_name}]}));
console.log(newItems);

Categories

Resources