React/JS/TS - Mapping dynamic data to a table duplicates? - javascript

const keys = data.getUser.keys;
console.log(keys)
for(let i = 0, l = keys.length; i < l; i++) {
var keyNickname = keys[i]['nickname'];
var keyExchange = keys[i]['exchange'];
var keyLivenet = keys[i]['livenet'];
console.log(keyNickname)
const createRow = (
rowId: number,
keyNickname: string,
keyExchange: string,
keyLivenet: string,
) => ({
cells: [
{ content: `${keyNickname}`, key: `${rowId}` },
{ content: `${keyExchange}`, key: `${rowId}` },
{ content: `${keyLivenet}`, key: `${rowId}` },
],
});
var rows = keys.map((a: number) =>
createRow(a, keys[i]['nickname'], keys[i]['exchange'], keys[i]['livenet'])
);
}
I'm so close yet so far. I'm just unsure how to properly map the data to the three rows!
Any assistance would be awesome, I'm trying to learn the hard way (and I have been.) though, I would love to solve this so I can move onto create other tables throughout the application.
EDIT: full page as requested:
const headSet = ['Nickname', 'Exchange', 'Livenet'];
const head = {
cells: headSet.map(headSet => ({
key: headSet,
content: headSet,
shouldTruncate: true,
isSortable: true,
})),
};
const keys = data.getUser.keys;
console.log(keys)
for(let i = 0, l = keys.length; i < l; i++) {
var keyNickname = keys[i]['nickname'];
var keyExchange = keys[i]['exchange'];
var keyLivenet = keys[i]['livenet'];
console.log(keyNickname)
const createRow = (
rowId: number,
keyNickname: string,
keyExchange: string,
keyLivenet: string,
) => ({
cells: [
{ content: `${keyNickname}`, key: `${rowId}` },
{ content: `${keyExchange}`, key: `${rowId}` },
{ content: `${keyLivenet}`, key: `${rowId}` },
],
});
var rows = keys.map((a: number) =>
createRow(a, keys[i]['nickname'], keys[i]['exchange'], keys[i]['livenet'])
);
}
return (
<PageLayout>
<TopNavigation height={60} isFixed={true}>
<TopNav />
</TopNavigation>
<Content>
<LeftSidebar width={240} isFixed={true}>
<SideBar />
</LeftSidebar>
<Main>
<div
style={{
marginLeft: 40,
marginRight: 40,
}}
>
<PageHeader
actions={actionsContent}
>
Connections
</PageHeader>
<DynamicTable
head={head}
rows={rows}
defaultPage={1}
loadingSpinnerSize="large"
isLoading={false}
isFixedSize
defaultSortKey="term"
defaultSortOrder="ASC"
onSort={() => console.log('onSort')}
onSetPage={() => console.log('onSetPage')}
rowsPerPage={!keys.length ? 10 : undefined}
/>
</div>
</Main>
</Content>
</PageLayout>
);
}
export default Keys;

You're passing bad arguments to your map function. A cleaner way would be this:
var rows = keys.map((key: any, i: number) =>
createRow(i, key.nickname, key.exchange, key.livenet)
);
Array.prototype.map takes a function with two arguments. The first is each element of the array as it is iterated over. The second is the index of the element in the array.
In your code, you're A) missing the first argument, and B) mistaking 'a' for 'i' in your function when dealing with the index.

Can you please provide the full source code for this page? This is tagged as react, but you aren't including any JSX, so I'm not sure if you meant to. I put an example below, but I have no idea of the structure of the data you imported.
const keys = data.getUser.keys
function TodosList(keys){
const renderRow = () => {
return keys.map(function(key, i){
return(
<tr>
<td>{key.keyNickname}</td>
<td>{key.keyExchange}</td>
<td>{key.keyLivenet}</td>
</tr>
)
})
}
return(
<div><h3>Key List?</h3>
<table className="table table-striped" style={{ marginTop: 20 }}>
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
</tr>
</thead>
<tbody>
{renderRow()}
</tbody>
</table>
</div>
)
}
export default TodosList

Related

ReactJS: Creating a "dynamic" render of Row and Col

I have an array of object with "id", "name", "value" that I pass to a component and it divided in row and col in this way:
export const RenderDetailRow = props => {
const columns = [];
props.content.forEach((content, idx) => {
columns.push(
<div className="col-sm py-3" key={`item_${idx}`}>
<b>{content.name + ': '}</b>
<Input type="text" name={content.name} id={content.id} readOnly value={content.value} />
</div>
);
if ((idx + 1) % props.display[0].number === 0) {
columns.push(<div className="w-100"></div>);
}
});
return (
<div className="row" style={{ margin: 30 }}>
{columns}
</div>
);
};
I have two kind of problem, the first:
Each child in a list should have a unique "key" prop.
I have inserted the key but I have this error.
If the number of field is odd I have a long Input, it is possible to create a empty field o something like this?
For example Date and Created By has every one 1/2 of the space, while Last Modified has 2/2. How can I do?
Thank you
EDIT.
props.display[0].number is only a number that i pass (for example 2,3,4) to obtain the number of cols
EDIT2:
Example of Array that I pass:
const Content = [
{
id: 'id',
name: 'ID',
value: realm.id,
},
{
id: 'realmId',
name: 'ID Realm',
value: realm.realmId,
},
{
id: 'name',
name: 'name',
value: realm.name,
}
]
const Display = [
{
number: 2,
},
];
so my render is:
render(
<RenderDetailRow content={Content} display={Display} />
)
For 1, you are missing key in if block
Try point 2:
function chunkArray(array, size) {
if(array.length <= size){
return [array]
}
return [array.slice(0,size), ...chunkArray(array.slice(size), size)]
}
export const RenderDetailRow = props => {
const columns = props.content.map((content, idx) => {
return (
<div
key={`item_${idx}`}
className="col-sm py-3"
>
<b>{content.name + ': '}</b>
<Input type="text" name={content.name} id={content.id} readOnly value={content.value} />
</div>
);
});
const rows = chunkArray(columns, NUMBER_OF_ROWS);
return rows.map((row, index) => (
<div className="row" key={index} style={{ margin: 30 }}>
{row[index]}
{row[index].length - NUMBER_OF_ROWS !== 0
? // (render row[index].length - NUMBER_OF_ROWS) empty columns here
: null}
</div>
))
};

How to insert index using if statement when using map function in react?

I am using redux and I am working on receiving data about membership information from action and processing it from components.
And try to process the data to a table.
I took data from user action and created a table through map functions... The incoming data contains
Data received as action ....
[{create_date : "2020-02-16T03:00:00Z", id:"test"},
{create_date : "2020-02-16T01:00:00Z", id:"test1"},
{create_date : "2020-02-14T03:00:00Z", id:"test2"},
{create_date : "2020-02-14T01:00:00Z", id:"test3"},
{create_date : "2020-02-14T00:00:01Z", id:"test4"},
{create_date : "2020-02-13T03:00:00Z", id:"test5"},
...]
As you can see, only create_date id is included.
I would like to order them by date and number them in order by next day after day.
For example, would like to print like this.
index create_date id
2 2020-02-16T03:00:00Z test
1 2020-02-16T01:00:00Z test1
3 2020-02-14T03:00:00Z test2
2 2020-02-14T01:00:00Z test3
1 2020-02-14T00:00:01Z test4
1 2020-02-13T03:00:00Z test5
How to insert index using if statement when using map function in react??
Mycode
render() {
const {user_list} = this.props;
console.log(user_list);
return (
<div className="animated fadeIn">
<Row>
<Col xl={12}>
<Card>
<CardHeader>
<i className="fa fa-align-justify"></i>userlist
</CardHeader>
<CardBody>
<Search searchUser={this.searchUser}/>
<Table responsive hover>
<thead>
<tr><th scope="col">Index</th>
<th scope="col">create_time</th>
<th scope="col">id/name</th>
</tr>
</thead>
<tbody>
{user_list.length > 0 && user_list.map((item, index) => (
<tr key={item.id.toString()}>
<td>{index}</td> //<<Does this change by date index.. if statement?
<td className={'datetime'}>
{item.hasOwnProperty('create_date') &&
<div>
{moment(item.create_date).format('YYYY-MM-DD')}<br/>
{moment(item.create_date).format('HH:mm')}
</div>
}
</td>
<td scope="row">
{item.displayName}
</td>
</tr>
))}
</tbody>
</Table>
</CardBody>
</Card>
</Col>
</Row>
</div>
)
}
....
Elaborate on comments of #James. I just changed a little bit.
You can group them first by date. And then flat it to a list you want. Plain javascirpt version:
let data = [{create_date : "2020-02-16T03:00:00Z", id:"test"},
{create_date : "2020-02-16T01:00:00Z", id:"test1"},
{create_date : "2020-02-14T03:00:00Z", id:"test2"},
{create_date : "2020-02-14T01:00:00Z", id:"test3"},
{create_date : "2020-02-14T00:00:01Z", id:"test4"},
{create_date : "2020-02-13T03:00:00Z", id:"test5"},
]
let groups = {}
for (let e of data.reverse()) {
let date = e.create_date.slice(0, 10)
if (groups[date]) {
let group = groups[date];
group.push({ index: group.length + 1, ...e });
} else {
groups[date] = [{ index: 1, ...e }]
}
}
const dates = Object.keys(groups).reverse()
const flatten = []
for (let date of dates) {
flatten.push(...(groups[date].reverse()))
}
console.log(flatten)
flatten should be logged as:
[ { index: 2, create_date: '2020-02-16T03:00:00Z', id: 'test' },
{ index: 1, create_date: '2020-02-16T01:00:00Z', id: 'test1' },
{ index: 3, create_date: '2020-02-14T03:00:00Z', id: 'test2' },
{ index: 2, create_date: '2020-02-14T01:00:00Z', id: 'test3' },
{ index: 1, create_date: '2020-02-14T00:00:01Z', id: 'test4' },
{ index: 1, create_date: '2020-02-13T03:00:00Z', id: 'test5' } ]
You should be able to render that directly in a map. Though I think it's better to store the flatten list as state or in redux store so that it won't do the transformation on every rerender.
Here's a minimal verifiable complete working demo (https://codepen.io/Alexander9111/pen/rNVeyyY), taking part of the excellent grouping and flattening function from #bedew and then sorting it into date and "index" to give the following output (in React CodePen):
JS:
const action_data = [
{create_date : "2020-02-16T03:00:00Z", id:"test"},
{create_date : "2020-02-16T01:00:00Z", id:"test1"},
{create_date : "2020-02-14T03:00:00Z", id:"test2"},
{create_date : "2020-02-14T01:00:00Z", id:"test3"},
{create_date : "2020-02-14T00:00:01Z", id:"test4"},
{create_date : "2020-02-13T03:00:00Z", id:"test5"}
];
const Demo = (props) => {
console.log(props.action)
let groups = {}
for (let e of props.action.reverse()) {
let date = e.create_date.slice(0, 10)
if (groups[date]) {
let group = groups[date];
group.push({ index: group.length + 1, ...e });
} else {
groups[date] = [{ index: 1, ...e }]
}
}
const dates = Object.keys(groups).reverse()
const flatten = []
for (let date of dates) {
flatten.push(...(groups[date].reverse()))
}
const user_list = flatten.sort((a,b) => {
if (moment(b.create_date).diff(moment(a.create_date), 'days') == 0){
return (b.index - a.index);
} else {
return 0;
}
});
console.log(user_list);
return(
<div>
<table>
<thead>
<tr>
<th scope="col">Index</th>
<th scope="col">create_time</th>
<th scope="col">id/name</th>
</tr>
</thead>
<tbody>
{user_list.length > 0 && user_list.map((item, index) => (
<tr key={item.id.toString()}>
<td>{item.index}</td>
<td className={'datetime'}>
{item.hasOwnProperty('create_date') &&
<div>
{moment(item.create_date).format('YYYY-MM-DD') + " "+ moment(item.create_date).format('HH:mm')}
</div>
}
</td>
<td scope="row">
{item.id}
</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
ReactDOM.render(
<Demo action={action_data}/>,
document.getElementById('root')
);
Important is the sort function:
const user_list = flatten.sort((a,b) => {
if (moment(b.create_date).diff(moment(a.create_date), 'days') == 0){
return (b.index - a.index);
} else {
return 0;
}
});
If the dates are not the same day, then don't sort, but if they are the same day then sort on the "index" created in the flatten array earlier.
Again demo here: https://codepen.io/Alexander9111/pen/rNVeyyY

Displaying the Sum of values in React JSX

Im trying to add up all of the calories in my array thats stored in the state.
id: shortid.generate(),
text: this.state.text,
calorie: this.state.calorie
This is the data structure that being stored in the state array meals
Im currently running a forEach and using reducer to add up the values but its saying "reduce" is not a function I'm not sure what i'm doing wrong.
class App extends Component {
state = {
meals: []
};
addMeal = meal => {
this.setState({
meals: [meal, ...this.state.meals]
});
};
onDelete = id => {
this.setState({
meals: this.state.meals.filter(meal => meal.id !== id)
});
};
render() {
return (
<div className="container">
<div className="jumbotron">
<h2>Calorie Counter</h2>
<hr />
<Form onsubmit={this.addMeal} />
<table className="table table-striped">
<thead>
<tr>
<th>Meal</th>
<th>Calories</th>
<th />
</tr>
</thead>
<tbody>
{this.state.meals.map(meal => (
<Meal
key={meal.id}
meal={meal}
onDelete={() => this.onDelete(meal.id)}
/>
))}
<tr>
<td>Total:</td>
<td>
{this.state.meals.forEach(meal =>
meal.reduce(function(y, x) {
return y + x;
}, 0)
)}
</td>
<td />
</tr>
</tbody>
</table>
</div>
</div>
);
}
}
Im trying to display the total of calories inside of the meal in jsx
Reduce is an array function, not a meal object function. Try replacing the forEach with the reduce.
meals.reduce((totalCalories, meal) => totalCalories + meal.calorie, 0)
The first reduce assumes calories are numbers, the second is if strings
const meals = [
{ calorie: 10},
{ calorie: 15},
{ calorie: 20}
];
const calorieTotal = meals.reduce((totalCalories, meal) => totalCalories + meal.calorie, 0);
console.log(calorieTotal); // 45 calories
const mealsAsStrings = [
{ calorie: '11'},
{ calorie: '12'},
{ calorie: '13'}
];
const calorieStringTotal = mealsAsStrings.reduce((totalCalories, meal) => totalCalories + parseInt(meal.calorie, 10), 0);
console.log(calorieStringTotal); // 36 calories
You can't use reduce method on array elements as it's an array method. In the example above you are looping into the array and trying to call reduce with each element of array which is not right. You can do as follows -
this.state.meals.reduce((accumulator, currentValue) => accumulator + currentValue)
Hope that helps.
UPDATE -
As you are trying to calculate calories from meal object array, we can do it as follows -
this.state.meals.reduce((accumulator, currentValue)=> accumulator + accumulator, currentValue.calorie,0);
Check the link for detail use of reduce method -
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
You can use yourArray.reduce like illustrated below:
Given this array in ReactJs
const App = () => {
const course ='Half Stack application development'
const empObj =[
{
employeename: 'Ndichu Kabata',
salary: 10000
},
{
employeename: 'George Githui',
salary: 70000
},
{
employeename: 'Super Omondi',
salary: 40000
}
]
return (
<div >
<Total employees={empObj } />
</div>
);
}
and you are required to compute total salary. Do as follows:
const Total =(props) =>{
const numbers = props.employees;
const saloTotal = numbers.reduce((totalHolder,m) => totalHolder + m.salary,0);
return(
<>
<p>Total Salary: {saloTotal}</p>
</>
)}

Sorting table in react. How it's possible when only one column works?

I'd like to sort table items (alphabetical) by clicking on table header. I've tried to do it myself, but it works really strange, only clicking on second header (priority) works... And when I click on first and third header, it sorts table items in order how they were put in there.
I use orderBy from lodash.
Here is my code, the slice of the full class.
const header = [
{name: "Task Name", id: "taskName"},
{name: "Priority", id: "priority"},
{name: "Done", id: "done"},
];
<TableHead>
<TableRow>
{header.map((el, i) => (
<TableCell key={i}>
<div
style={{
display: 'flex',
alignItems: 'center'
}}
onClick={() => this.props.handleSort(el.id)}
>
{el.name}
{
this.props.columnToSort === el.id
? (this.props.sortDirection === 'asc'
? <UpArrow/>
: <DownArrow/>
)
: null
}
</div>
</TableCell>
))}
<TableCell/>
</TableRow>
</TableHead>
And logics in different class, "connected" by props.
const invertDirection = {
asc: "desc",
desc: "asc",
};
class...
state = {
columnToSort: '',
sortDirection: 'desc',
};
handleSort = (columnName) => {
this.setState({
columnToSort: columnName,
sortDirection:
this.state.columnToSort === columnName
? invertDirection[this.state.sortDirection]
: 'asc',
});
};
props
tableData={orderBy(
this.state.tableData,
this.state.columnToSort,
this.state.sortDirection
)}
handleSort = {this.handleSort}
columnToSort = {this.state.columnToSort}
sortDirection = {this.state.sortDirection}
I know it may be hard to read, because I've got many components, but pasted only things I use to do a sort.
Can you tell me why when clicking on second table header priority, sorting works, and when clicking on other headers it don't?
If you have any better ideas for sorting, please let me know.
I'm hopping i understand your goal here, you are trying to sort the data via a click on the table's headers and toggle it to sort it in ascending or descending manner.
If this is correct i would take a simpler approach.
Sorting by dynamic key
You can create a Th component of your own that will take an onClick prop and an id prop where the id is the name of the object's key.
When the Th is clicked it will invoke the handler and will pass the id (the object's key) to the handler.
This way you can sort on the key that got passed by the child.
Ascending Or Descending
We only have 2 options for sorting - Ascending or Descending. This means we can use a Boolean instead of a string (that will simplify our logic a bit).
So after each click on a given Th we will set a new Boolean object in our state when the key being the id of the Th and we will flip it's value.
This way we can conditionally sort by the given key either in an ascending or descending way.
Here is a small running example:
const data = [
{ name: 'John', age: 32 },
{ name: 'Mike', age: 27 },
{ name: 'Jane', age: 31 },
{ name: 'Criss', age: 25 },
{ name: 'Tom', age: 18 },
]
class Th extends React.Component {
handleClick = () => {
const { onClick, id } = this.props;
onClick(id);
}
render() {
const { value } = this.props;
return (
<th onClick={this.handleClick}>{value}</th>
)
}
}
class App extends React.Component {
state = {
users: data
}
handleSort = (id) => {
this.setState(prev => {
return {
[id]: !prev[id],
users: prev.users.sort((a, b) => prev[id] ? a[id] < b[id] : a[id] > b[id] )
}
});
}
render() {
const { users } = this.state;
return (
<table>
<thead>
<tr>
<Th onClick={this.handleSort} id="name" value="Name" />
<Th onClick={this.handleSort} id="age" value="Age" />
</tr>
</thead>
<tbody>
{
users.map(user => (
<tr>
<td>{user.name}</td>
<td>{user.age}</td>
</tr>
))
}
</tbody>
</table>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

React data components table does not render HTML

https://github.com/carlosrocha/react-data-components package does not allow sending html into a td cell. See:
My goal is hyperlink to that product.
My use is:
import React from 'react';
var DataTable = require('react-data-components').DataTable;
import PlainTable from './PlainTable'
class ReduxDataTable extends React.Component {
constructor(props) {
super(props);
}
processHeaders(){
var columns = [];
for (let i = 0; i < this.props.data.headers.length; i++){
var header = this.props.data.headers[i];
var item = {title: header, prop: header};
columns.push(item);
}
return columns;
}
render() {
var dataList = this.props.data.data;
console.log("datalist is", dataList);
console.log("datalist length is", dataList.length);
var headerList = this.processHeaders();
if(dataList.length > 2) {
return (
<DataTable
keys="name"
columns={headerList}
initialData={dataList}
initialPageLength={20}
initialSortBy={{ prop: headerList[0].title, order: 'descending' }}
pageLengthOptions={[ 20, 60, 120 ]}
/>
);
}
else {
return (
<PlainTable
headers={headerList}
rows={dataList}
/>
);
}
}
}
export { ReduxDataTable as default };
then just
return (
<div className="card">
<h2 className="style-1">Detailed Report</h2>
<br/>
<h2 className="style-1:after">Data about products </h2>
<ReduxDataTable data={data}/>
</div>
)
Plain table is a <table> in case there's few products.
The package does not show any "htmlTrue" option, as searching "html" show nothing useful. I'm getting the same issue with any html at all:
I'm not opposed to forking it, but is there a simple way to use this package and declare html here?
I didn't use that component, but looking through the code, it seems that you can use a render function to do what you need. See here: https://github.com/carlosrocha/react-data-components/blob/3d092bd375da0df9428ef02f18a64d056a2ea5d0/src/Table.js#L13
See the example here https://github.com/carlosrocha/react-data-components/blob/master/example/table/main.js#L17
Relevant code snippet:
const renderMapUrl =
(val, row) =>
<a href={`https://www.google.com/maps?q=${row['lat']},${row['long']}`}>
Google Maps
</a>;
const tableColumns = [
{ title: 'Name', prop: 'name' },
{ title: 'City', prop: 'city' },
{ title: 'Street address', prop: 'street' },
{ title: 'Phone', prop: 'phone', defaultContent: '<no phone>' },
{ title: 'Map', render: renderMapUrl, className: 'text-center' },
];
return (
<DataTable
className="container"
keys="id"
columns={tableColumns}
initialData={data}
initialPageLength={5}
initialSortBy={{ prop: 'city', order: 'descending' }}
pageLengthOptions={[ 5, 20, 50 ]}
/>
);
Try adding the render property to your dataList. Maybe something like this
var dataList = this.props.data.data;
for (let i=0; i<dataList.length; i++)
dataList[i].render = function(val, row) {return (
<a href={row.href}>row.title</a>
)}

Categories