I have the following dataset that I am trying to get values from, using JavaScript.
My code is as follows:
{companies.map(({ matches }) => (
<Company
key={matches}
name={matches}
symbol={matches}
/>
))}
I have tried to get the values by providing the key in the map function as follows:
name={matches['2. name']}
My question is, what syntax should I be using to get a value where the key has spacing and full-stops?
Your issue is this:
companies.map(({ matches }) => { … })
^^^^^^^^^^^
This is saying that for each item in the companies array, use a property named matches (through parameter destructuring), which, from what you're showing, doesn't exist.
Instead, I think you meant this:
companies.map(( matches ) => { … });
// or shorter:
// companies.map( matches => { … });
Related
Is there a way to prevent errors from being thrown while filtering?
The below function sometimes fails at conversationMember.Name.toLowerCase() when there is no conversationMember.
If it helps, this is also a computed property in a Vue application.
Should you need more information, please just ask!
filteredConversations() {
var self = this;
var filteredConvos = self.conversations;
filteredConvos = filteredConvos.filter(conversation => {
return conversation.MembershipData.some(conversationMember => {
return conversationMember.Name.toLowerCase().includes(
self.conversationSearchTerm.toLowerCase()
);
});
});
return filteredConvos;
},
This doesn't seem to have anything to do with arrays.
From your code I understand conversationMember.Name is supposed to be a string (because you're calling .toLowerCase() on it), which means incudes here is not Array.prototype.includes, but String.prototype.includes, especially since self.conversationSearchTerm seems to also be a string (you're also calling .toLowerCase() on it).
So, the problem is you're using includes on something that should be a string but is not. The simple fix is to default it to an empty string when it's falsy:
return (conversationMember.Name || '').toLowerCase().includes(
(self.conversationSearchTerm || '').toLowerCase()
);
As a side note, you don't need the var self = this;. this is available inside your filter since the filter is an arrow function. So your function (I'm guessing it's a computed but it can as well be a method) could look like this:
filteredConversations() {
return this.conversations.filter(c =>
c.MembershipData.some(md =>
(md.Name || '').toLowerCase().includes(
(this.conversationSearchTerm || '').toLowerCase()
)
)
);
}
One last note: this will still fail if any of your conversations does not have a MembershipData holding an array. To get around that, you could default it to an empty array on the fly:
...
(c.MembershipData || []).some(md =>
...
As expected, any conversation without an array in MembershipData will be filtered out by the function (not included in the result) - because .some(condition) will return false when called on an empty array.
Using Logger.log(response.data.phone), I'm getting this in my log:
[{label=work, primary=true, value=5558675309}, {label=work, value=6108287680, primary=false}, {value=6105516373, label=work, primary=false}]
What I want is to return the two phone numbers as 5558675309, 6108287680.
I've tried Logger.log(response.data.phone.value) and that doesn't work. I've tried ...phone['value'], I've tried ...phone[0].value and this one does return the first phone number 5558675309. But I would like it to return all of the value values whenever I put in the phone key. So how would I modify the logger?
response.data.phone is an array you can try looping through it:
Logger.log(response.data.phone.map(phone => phone.value).join(', '));
const response = {data: {phone : [{label:'work', primary:true, value:5558675309}, {label:'work', value:6108287680, primary:false}, {value:6105516373, label:'work', primary:false}] } }
const Logger = { log : console.log};
Logger.log(response.data.phone.map(phone => phone.value).join(', '));
response.data is an array, response.data.phone does not exist. What you want is response.data[n].phone for an integer n. You can do this with a forEach loop.
response.data.forEach((element) => Logger.log(element.phone.value));
If for whatever reason you need support for older browsers you can use the older function syntax:
response.data.forEach(function(element){
Logger.log(element.phone.value)
});
In my ReactJS application I am getting the mobile numbers as a string which I need to break and generate a link for them to be clickable on the mobile devices. But, instead I am getting [object Object], [object Object] as an output, whereas it should be xxxxx, xxxxx, ....
Also, I need to move this mobileNumbers function to a separate location where it can be accessed via multiple components.
For example: Currently this code is located in the Footer component and this code is also need on the Contact Us component.
...
function isEmpty(value) {
return ((value === undefined) || (value === null))
? ''
: value;
};
function mobileNumbers(value) {
const returning = [];
if(isEmpty(value))
{
var data = value.split(',');
data.map((number, index) => {
var trimed = number.trim();
returning.push(<NavLink to={`tel:${trimed}`} key={index}>{trimed}</NavLink>);
});
return returning.join(', ');
}
return '';
};
...
What am I doing wrong here?
Is there any way to create a separate file for the common constants / functions like this to be accessed when needed?
First question:
What am I doing wrong here?
The issue what you have is happening because of Array.prototype.join(). If creates a string at the end of the day. From the documentation:
The join() method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string. If the array has only one item, then that item will be returned without using the separator.
Think about the following:
const navLinks = [{link:'randomlink'}, {link:'randomlink2'}];
console.log(navLinks.join(','))
If you would like to use concatenate with , then you can do similarly like this:
function mobileNumbers(value) {
if(isEmpty(value)) {
const data = value.split(',');
return data.map((number, index) => {
const trimed = number.trim();
return <NavLink to={`tel:${trimed}`} key={index}>{trimed}</NavLink>;
}).reduce((prev, curr) => [prev, ', ', curr]);
}
return [];
};
Then you need to use map() in JSX to make it work.
Second question:
Is there any way to create a separate file for the common constants / functions like this to be accessed when needed?
Usually what I do for constants is that I create in the src folder a file called Consts.js and put there as the following:
export default {
AppLogo: 'assets/logo_large.jpg',
AppTitle: 'Some app name',
RunFunction: function() { console.log(`I'm running`) }
}
Then simply import in a component when something is needed like:
import Consts from './Consts';
And using in render for example:
return <>
<h1>{Consts.AppTitle}</h1>
</>
Similarly you can call functions as well.
+1 suggestion:
Array.prototype.map() returns an array so you don't need to create one as you did earlier. From the documentation:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
I hope this helps!
I have a Map object:
let dateJobMap = new Map();
for (let jobInArray of this.state.jobs) {
let deliveryDate: Date = new Date(jobInArray.DeliveryDate);
let deliveryDateString: string = deliveryDate.toLocaleDateString("en-US");
if (dateJobMap.has(deliveryDateString)) {
let jobsForDate: IDeliveryJob[] = dateJobMap.get(deliveryDateString);
jobsForDate.push(jobInArray);
}
else {
let jobsForDate: IDeliveryJob[] = [jobInArray];
dateJobMap.set(deliveryDateString, jobsForDate);
}
}
In my render method, I want to call a TruckJobComp object for each delivery job in the value's array to display it:
<div className={ styles.column }>
<p className={ styles.description }>{escape(this.props.description)}</p>
{
dateJobMap.forEach(function(jobsForDate, dateString) {
jobsForDate.map(job => (
<TruckJobComp job = { job } />
))
})
}
</div>
This seems like it should work but doesn't. It never creates a TruckJobComp. I do a .forEach iteration on my Map, and for each value's array, I use .map to get the individual job object to send to TruckJobComp object.
When I create a temp array to grab the jobs from the last loop:
let tempJobs: IDeliveryJob[];
and in the loop add in:
if (dateJobMap.has(deliveryDateString)) {
let jobsForDate: IDeliveryJob[] = dateJobMap.get(deliveryDateString);
jobsForDate.push(jobInArray);
tempJobs = jobsForDate;
}
and then use that array in the render:
<div className={ styles.column }>
<p className={ styles.description }>{escape(this.props.description)}</p>
{
tempJobs.map(job => (
<TruckJobComp job = { job }/>
))
}
</div>
It displays as expected.
I do have a warnings in Visual Studio Code:
Warning - tslint - ...\TruckDeliverySchedule.tsx(104,38): error no-function-expression: Use arrow function instead of function expression
I don't know enough to understand. Line 104 corresponds with:
dateJobMap.forEach(function(jobsForDate, dateString) {
I am very new to this so I'm not 100% sure how most of this works. Just trying to put pieces I've learned together to get things to work.
Second Edit:
{escape(this.props.description)}
{
[...dateJobMap.keys()].map(jobsForDate => // line 154
jobsForDate.map(job => (
<TruckJobComp job = { job } />
))
)
}
Produces error:
[09:06:56] Error - typescript - src\...\TruckDeliverySchedule.tsx(154,27): error TS2461: Type 'IterableIterator<any>' is not an array type.
dateJobMap.forEach(...) returns undefined, so it cannot be mapped to a collection of elements.
ES6 maps have forEach method for compatibility purposes (generally for..of is preferred to iterate over iterables) and don't have map method. A map should be converted to array first, then it could be mapped to an element. Since values aren't used, only keys need to be retrieved:
{
[...dateJobMap.keys()].map(jobsForDate =>
jobsForDate.map(job => (
<TruckJobComp job = { job } />
))
)
}
All this warning is saying is that instead of using the syntax function(jobsForDate, dateString) {} you should use the syntax (jobsForDate, dateString) => {}.
The reason could be the way this is scoped in arrow functions versus function expressions. See this post.
My guess as to the reason your first approach didn't work but your second one did is that forEach doesn't actually return an array, and if it did, calling map within forEach would return an array of arrays (but, again, it doesn't). Not sure how React would handle that, but React does know how to handle a single array, which is what your last approach returns.
I have an observable array and would like to get the sum of a property values in that array. My array is defined as:
public bookStores$: Observable;
I was going to do a simple for loop and calculate the sum, but I get a syntax error when trying to use the count property of my array:
Operator '<' cannot be applied to types 'number' and '<T>(this: Observable<T>, predicate?: (value: T, index: number, source: Observable<T>)=>boolean)...
This occurs when I do:
for (let i = 0; i < this.bookStores$.count; i++){ }
Every item in my array of BookStore objects has a property called numberOfBooks. What is the proper way to get the sum of those values contained on each BookStore object in my BookStore array?
This is why you're getting unexpected results for Observable.count
To get the array lenght of the results, you need to do, something like this:
BookStoreService.ts
...
getBookStore() : Observable<Bookstore> {
this.bookstore$ = this.http.get(...)
.map( (response:Response) => response.json() )
.map( response => response.bookstore); // optional depends if JSON payload you want is wrapped inside some other container
return this.bookstore$;
}
Component.ts
...
bookstore$ : Observable<Bookstore>;
bookstores: Bookstore[];
numberOfBookStores:number;
constructor(private bookService:BookService) {}
..
this.bookService.getJobs()
.subscribe(
bookstores => {this.bookstores = bookstores;
this.numberOfBookstores = this.bookstores.length;
},
err => { this.bookstores = [] as BookStore[];
// Caters for 404 etc error - be sure to be more robust in final code as you may want to indicate types of error back to user.
},
() => {
}
);
Update:
If you only need to loop through the list in yourHTML template, then
then defining the bookstores array as a property would not be necessary. I did this to illustrate how to get the size of the returned collection of bookstores.
You can use this type of syntax:
<tr *ngFor="let bookstore of (bookstore$ |async) as bookstores;
trackBy bookstore?.id; let i = index">
<td>{{bookstore.numberofBooks}}
<tr/>
You can find out more about:
*ngFor trackBy, even, odd, first, last here.
Using Async pipe for entire block of html template with AS here
Furthermore have a look at libraries like Lodash and Underscore for summing count of number of books. I've not used Underscore myself.
Here's a simple example to get you started.
If you want to get more adventurous have a look at this Functional Programming in Javascript Tutorial