Show dynamically added row in expanded form in ant tables - javascript

I have an ant table where I pass a parameter "defaultExpandAllRows=true" which renders the table in an expanded form.
<Table
columns={columns}
pagination={false}
expandable={{
expandedRowRender: (record) => (
<p style={{ margin: 0 }}>{record.description}</p>
),
defaultExpandAllRows: true,
rowExpandable: (record) => record.name !== "Not Expandable"
}}
dataSource={customScopeTableData}
/>
My use case is to show the dynamically added row in expanded form.
Here is the working sample code
https://codesandbox.io/s/dynamic-expandable-row-issue-f6bn5?file=/index.js
I couldn't find something in the API doc.
Any help on this would be appreciable.

I think you should use expandedRowKeys instead of defaultExpandAllRows. Because of defaultExpandAllRows only apply to the initial data, so newly added rows won't be affected.
expandable={{
expandedRowRender: (record) => (
<p style={{ margin: 0 }}>{record.description}</p>
),
rowExpandable: (record) => record.name !== "Not Expandable",
expandedRowKeys: expandedRowKeys,
onExpand: (expanded, record) => {
updateExpandedRowKeys({ record });
}
}}
First create a state for expandedRowKeys and a function for update expandedRowKeys
const [expandedRowKeys, setExpandedRowKeys] = useState(data.map(({ key }) => key));
const updateExpandedRowKeys = ({ record }) => {
const rowKey = record.key;
const isExpanded = expandedRowKeys.find(key => key === rowKey);
let newExpandedRowKeys = [];
if (isExpanded) {
newExpandedRowKeys = expandedRowKeys.reduce((acc, key) => {
if (key !== rowKey) acc.push(key);
return acc;
}, []);
} else {
newExpandedRowKeys = expandedRowKeys;
newExpandedRowKeys.push(rowKey);
}
setExpandedRowKeys(newExpandedRowKeys);
};
Then insert new expandedRowKey when new row added
const addCustomField = () => {
const newIndex = customScopeTableData.slice(-1)[0].key + 1;
setCustomScopeTableData([...customScopeTableData, newEntry(newIndex)]);
setExpandedRowKeys([...expandedRowKeys, newIndex])
};
Column key for newEntry should be unique, Date.now() seems will be duplicated. I have changed it to incremental index.
const newIndex = customScopeTableData.slice(-1)[0].key + 1;
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Table, Button } from "antd";
const columns = [
{ title: "Name", dataIndex: "name", key: "name" },
{ title: "Age", dataIndex: "age", key: "age" },
{ title: "Address", dataIndex: "address", key: "address" },
{
title: "Action",
dataIndex: "",
key: "x",
render: () => <a>Delete</a>
}
];
const data = [
{
key: 1,
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
description:
"My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park."
},
{
key: 2,
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
description:
"My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park."
},
{
key: 3,
name: "Not Expandable",
age: 29,
address: "Jiangsu No. 1 Lake Park",
description: "This not expandable"
},
{
key: 4,
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park",
description:
"My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park."
}
];
const newEntry = (key) => {
return {
key: key,
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
description:
"My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park."
}
};
const TableComponent = () => {
const [customScopeTableData, setCustomScopeTableData] = useState(data);
const addCustomField = () => {
const newIndex = customScopeTableData.slice(-1)[0].key + 1;
setCustomScopeTableData([...customScopeTableData, newEntry(newIndex)]);
setExpandedRowKeys([...expandedRowKeys, newIndex])
};
const [expandedRowKeys, setExpandedRowKeys] = useState(data.map(({ key }) => key));
const updateExpandedRowKeys = ({ record }) => {
const rowKey = record.key;
const isExpanded = expandedRowKeys.find(key => key === rowKey);
let newExpandedRowKeys = [];
if (isExpanded) {
newExpandedRowKeys = expandedRowKeys.reduce((acc, key) => {
if (key !== rowKey) acc.push(key);
return acc;
}, []);
} else {
newExpandedRowKeys = expandedRowKeys;
newExpandedRowKeys.push(rowKey);
}
setExpandedRowKeys(newExpandedRowKeys);
};
return (
<>
<Table
columns={columns}
pagination={false}
expandable={{
expandedRowRender: (record) => (
<p style={{ margin: 0 }}>{record.description}</p>
),
rowExpandable: (record) => record.name !== "Not Expandable",
expandedRowKeys: expandedRowKeys,
onExpand: (expanded, record) => {
updateExpandedRowKeys({ record });
}
}}
dataSource={customScopeTableData}
/>
<Button type="dashed" onClick={() => addCustomField()}>
Add field
</Button>
</>
);
};
ReactDOM.render(<TableComponent />, document.getElementById("container"));

Related

react js state in parent component isn't re rendering in child component

so i am trying to make a filter for my products. And for that i initialised a state in a component called Product that has all the filters. And then, based on the input of the checkboxes that are in a child component that is called Category, the filter state changes.
here is my code :
Product.jsx:
import { useState } from "react";
import Filter from "./Filter.jsx";
import styles from "../styles/products.module.css";
const products = {
prod1: {
id: 1,
name: "prod1",
category: "laptops",
price: "80",
description: { brand: "lenovo", processor: "intel core i7" },
},
prod2: {
id: 2,
name: "prod2",
category: "laptops",
price: "10",
description: { brand: "lenovo", processor: "intel core i7" },
},
prod3: {
id: 3,
name: "prod3",
category: "laptops",
price: "100",
description: { brand: "msi", processor: "intel core i5" },
},
prod4: {
id: 4,
name: "prod4",
category: "laptops",
price: "200",
description: { brand: "msi", processor: "intel core i3" },
},
prod5: {
id: 5,
name: "prod5",
category: "phones",
price: "50",
description: { brand: "samsung", ram: "8gb", storage: "64gb" },
},
prod6: {
id: 6,
name: "prod6",
category: "phones",
price: "50",
description: { brand: "infinix", ram: "4gb", storage: "128gb" },
},
prod7: {
id: 7,
name: "prod7",
category: "phones",
price: "50",
description: { brand: "oppo", ram: "8gb", storage: "256gb" },
},
prod8: {
id: 8,
name: "prod8",
category: "accessories",
price: "99",
description: { type: "keyboard" },
},
prod9: {
id: 9,
name: "prod9",
category: "accessories",
price: "75",
description: { type: "mouse" },
},
};
function Products() {
const filter = {
laptops: {
brand: [],
processor: [],
},
phones: {
brand: [],
ram: [],
storage: [],
},
accessories: {
type: [],
},
};
const [filterList, setFilterList] = useState(filter);
const [category, setCategory] = useState("all");
const [range, setRange] = useState(9999);
const getFilterState = () => {
return filterList;
};
const setFilterState = (list) => {
setFilterList(list);
};
return (
<div className={styles.productsContainer}>
<Filter
range={[range, setRange]}
category={[category, setCategory]}
getfilterList={getFilterState}
setFilterState={setFilterState}
/>
<table className={styles.productsListContainer}>
<tr className={styles.tableColumn}>
<th className={styles.tableRow}>id</th>
<th className={styles.tableRow}>Name</th>
<th className={styles.tableRow}>price</th>
</tr>
{Object.keys(products).map((key) => {
let flag = true;
const product = products[key];
const categ = product.category;
if (
(categ === category || category === "all") &&
product.price <= range
) {
Object.entries(filter[categ]).forEach(([key, value]) => {
if (
!(
value.length === 0 || value.includes(product.description[key])
)
) {
flag = false;
}
});
if (flag) {
return (
<tr className={styles.tableColumn} key={product.id}>
<td className={styles.tableRow}>{product.id}</td>
<td className={styles.tableRow}>{product.name}</td>
<td className={styles.tableRow}>{product.price}</td>
</tr>
);
}
return null;
}
return null;
})}
</table>
</div>
);
}
export default Products;
Filter.jsx:
import CategoryComponent from "./Category";
import styles from "../styles/filter.module.css";
function Filter({
filters,
dispatch,
category,
getfilterList: filterList,
setFilterState: setFilterList,
range: rangeState,
}) {
const [Category, setCategory] = category;
const [Range, setRange] = rangeState;
const handleCategoryChanged = (event, categ, key) => {
let list = filterList();
if (list[categ][key].includes(event.currentTarget.value)) {
list[categ][key] = list[categ][key].filter(
(val) => val !== event.currentTarget.value
);
} else {
list[categ][key].push(event.currentTarget.value);
}
setFilterList(list);
if (Category !== categ) {
setCategory(categ);
} else {
let flag = false;
Object.keys(filterList()[categ]).forEach((key) => {
const obj = filterList()[categ][key];
if (obj.length !== 0) flag = true;
});
if (!flag) {
setCategory("all");
}
}
};
const handleRangeChanged = (event) => {
setRange(event.currentTarget.value);
};
return (
<div className="filter-container">
<h1>{Category}</h1>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
category={Category}
handleCategoryChanged={handleCategoryChanged}
categories={{
brand: ["lenovo", "msi", "asus"],
processor: ["intel core i3", "intel core i5", "intel core i7"],
}}
>
laptops
</CategoryComponent>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
handleCategoryChanged={handleCategoryChanged}
category={Category}
categories={{
brand: ["samsung", "infinx", "oppo"],
ram: ["8gb", "4gb", "2gb"],
storage: ["64gb", "128gb", "256gb"],
}}
>
phones
</CategoryComponent>
<CategoryComponent
filterList={filterList()}
setFilterList={setFilterList}
category={Category}
handleCategoryChanged={handleCategoryChanged}
categories={{
type: ["keyboard", "mouse", "screen"],
}}
>
accessories
</CategoryComponent>
<div className={styles.priceRangeContainer}>
<input
type="range"
min="0"
max="9999"
className={styles.priceRange}
value={Range}
onChange={handleRangeChanged}
/>
<span>{Range}DT</span>
</div>
</div>
);
}
export default Filter;
Category.jsx
import React from "react";
import { useState, useEffect } from "react";
import styles from "../styles/category.module.css";
import arrow from "../img/angle-up-solid-svgrepo-com.svg";
export default function Category({
handleCategoryChanged,
children,
categories,
category,
filterList,
setFilterList,
}) {
const [hiddenClass, setHiddenClass] = useState(styles.hidden);
const handleClick = () => {
if (hiddenClass) setHiddenClass("");
else setHiddenClass(styles.hidden);
};
return (
<div>
<h5>{category}</h5>
<div onClick={handleClick} className={styles.categoryBox}>
{children} <img style={{ width: 15 }} src={arrow} alt="" />
</div>
<div className={styles.hiddenScroll + " " + hiddenClass}>
{Object.entries(categories).map(([key, value]) => {
return (
<div key={key}>
<h5>{key}:</h5>
<ul>
{value.map((val) => {
console.log(filterList);
return (
<li key={val}>
<input
type="checkbox"
name={val}
id={val}
value={val}
onChange={(e) =>
handleCategoryChanged(e, children, key)
}
/>
<label htmlFor={val}>{val}</label>
</li>
);
})}
</ul>
</div>
);
})}
</div>
</div>
);
}
The problem is when i run my program the state changes when i check an only one checkbox, but when i click on more checkboxes nothing changes.
EDIT:
my components hierarchy :
products.jsx
filter.jsx
Category.jsx
in products.jsx :
i have a state filterList that has all the filters i need.
const filter = {
laptops: {
brand: [],
processor: [],
},
phones: {
brand: [],
ram: [],
storage: [],
},
accessories: {
type: [],
},
};
const [filterList, setFilterList] = useState(filter);
in Filter.jsx:
i have state filterList that was passed as a prop from the Products component.
and a function that handles any checkbox change from it's child component.
const handleCategoryChanged = (event, categ, key) => {
let list = filterList();
//testing if the value is already in the filterList if true we remove it
if (list[categ][key].includes(event.currentTarget.value)) {
list[categ][key] = list[categ][key].filter(
(val) => val !== event.currentTarget.value
);
} else {
// else we add it to the list
list[categ][key].push(event.currentTarget.value);
}
setFilterList(list);
Category.jsx:
here i created all my checkboxes with the onChange event.
But the problem is when more than one checkbox gets changed, my filterList state does not get re rendered.
You missed the useState in your Filter.jsx
Replacing
const [Category, setCategory] = category;
const [Range, setRange] = rangeState;
With
const [Category, setCategory] = useState(category);
const [Range, setRange] = useState(rangeState);
A small working example could be found at code sandbox
⚠️ You did not provide the parent of Filter.jsx so in the example only the laptop brands are working

How to update input value on click of button from another page?

In React project I've certain list of data populated in grid component. The grid has columns of varied data types like text, input field, link etc. In one such column an input field is mapped which has its respective default values.
Now my intention is to change name of that particular input field of a particular record which navigates to another page which, has a button when clicked changes the name on the grid, but, the name of all records are changed. I need to change name of only that single record which is clicked and navigated based on its 'id'. Please refer to the code below.
const newCompData = [
{
id: 1,
comp: "McDonalds",
feedback: "Best Food Chain",
name: "Mike John",
est: "YYYY/MM",
store: "Burger Store"
},
{
id: 2,
comp: "KFC",
feedback: "Best Chicken Products",
store: "Chicken Food",
name: "Steve Williams",
est: "YYYY/MM"
},
{
id: 3,
comp: "Dominos",
feedback: "Best Pizza Store",
store: "Pizza Store",
name: "Mark Rays",
est: "YYYY/MM"
},
{
id: 4,
comp: "Star Bucks",
feedback: "Best Coffee Store",
store: "Coffee Store",
name: "Patrick Right",
est: "YYYY/MM"
},
{
id: 5,
comp: "Burger King",
feedback: "Best Burgers",
store: "Burger Store",
name: "Williams Wills",
est: "YYYY/MM"
},
{
id: 6,
comp: "Lays",
feedback: "Best Chips Factory",
store: "Chips Store",
name: "Sam Andrews",
est: "YYYY/MM"
}
];
const [dataAll, setDataAll] = useState([]);
useEffect(() => {
const newData = newCompData?.map((data) => {
return [
{ id: data.id },
data.comp,
data.store,
data.name,
data.est,
data.feedback
];
});
setDataAll(newData);
}, []);
return (
<div className="App">
<Table newData={dataAll} />
</div>
);
};
As seen above.... a list of records are mapped into the table component
Here is the table component, where all the data is mapped
const Table = ({ newData }) => {
useEffect(() => {
setGridData({
data: newData,
page_info: {
total_pages: 5,
current_page: 1
}
});
}, [newData]);
let GridConfig = {};
GridConfig = TableConfig;
const [gridConfigData, setGridConfigData] = useState(GridConfig);
const [gridData, setGridData] = useState(newData);
return (
<>
<Grid GridConfig={gridConfigData} GridData={gridData} />
</>
);
};
Following is the Grid component in table format (only showing the required part)
const Grid = (props) => {
let colConfig = props.GridConfig.column_config;
let gridData = props.GridData?.data;
const { newValue } = useContext(GlobalContext);
const navigate = useNavigate();
return (
....
....
....
{colConfig[cIndex].data_type === "input_text" &&
!colConfig[cIndex].cell_click_callback && (
<input
type="text"
defaultValue={
newValue != undefined ? newValue : colData
}
/>
)}
....
....
....
)
}
export default Grid
This is the page when clicked on record is navigated here.
const Test = () => {
const location = useLocation();
const navigate = useNavigate();
const newId = location.state?.id;
const { setNewValue } = useContext(GlobalContext);
const handleClick = () => {
navigate("/");
// When clicked on this setValue shows the same value across all records
setNewValue("John Spencer");
};
return (
<>
<h2>Test Page</h2>
<p>ID: {newId}</p>
<button onClick={handleClick}>Go Back</button>
</>
);
};
I want to show the value for input text which was clicked on Test page. Only specific record should be updated. What is the best solution to tackle this issue?
Please refer to the Codesandbox link: https://codesandbox.io/s/elated-varahamihira-xpjtdb

Render data from array of object based on a particular property as a heading in React

I have an array of object like below -
{
name:"Thor",
universe:"Marvel",
type:"God"
},
{
name:"Batman",
universe:"DC",
type:"Human"
},
{
name:"Iron man",
universe:"Marvel",
type:"Human"
},
];
Now, let's suppose that I want to render the objects such that the details of type Human are together, and the details of type God are together.
Basically, I want to display data based on the type property like below -
**Human**
name: Batman universe:DC
name: Iron man universe:Marvel
**God**
name: Thor universe: Marvel
I can help you with classification.
You can do render your self.
const data=[{
name:"Thor",
universe:"Marvel",
type:"God"
},
{
name:"Batman",
universe:"DC",
type:"Human"
},
{
name:"Iron man",
universe:"Marvel",
type:"Human"
},
];
const category={};
for(let i of data){
category[i.type]??=[]
category[i.type].push(i)
}
Category like:
{
"God":[
{
"name":"Thor",
"universe":"Marvel",
"type":"God"
}
],
"Human":[
{
"name":"Batman",
"universe":"DC",
"type":"Human"
},
{
"name":"Iron man",
"universe":"Marvel",
"type":"Human"
}
]
}
I would first format the data type to an easy to use data type:
See example below:
function App() {
const characters = [
{
name: "Thor",
universe: "Marvel",
type: "God",
},
{
name: "Batman",
universe: "DC",
type: "Human",
},
{
name: "Iron man",
universe: "Marvel",
type: "Human",
},
];
const charactersObj = {};
characters.forEach((character) => {
const { type } = character;
if (charactersObj[type]) {
const currentArray = [...charactersObj[type]];
currentArray.push(character);
charactersObj[type] = currentArray;
} else {
charactersObj[type] = [character];
}
});
return (
<div>
{Object.entries(charactersObj).map(([charType, charData]) => {
return (
<div>
<p> {charType}</p>
{charData.map(({name, universe}) => <p>name: {name} universe: {universe} </p>)}
</div>
);
})}
</div>
);
}
Without changing your data structure, you can just map over the type
const App = () => {
return (
<div>
Human:
<div>
{heroes.map((hero) =>
hero.type === "Human" ? <div>{hero.name}</div> : null,
)}
</div>
God:
<div>
{heroes.map((hero) =>
hero.type === "God" ? <div>{hero.name}</div> : null,
)}
</div>
</div>
);
};
you can try something like this
const data = [{
name:"Thor",
universe:"Marvel",
type:"God"
},
{
name:"Batman",
universe:"DC",
type:"Human"
},
{
name:"Iron man",
universe:"Marvel",
type:"Human"
},
];
const transform = data => data.reduce((res, {type, ...rest}) => {
return {
...res,
[type]: [...(res[type] || []), rest]
}
},{})
const toTemplate = (type, data) => {
return `**${type}**
${data.map(({name, universe}) => `name: ${name} universe: ${universe}`).join('\n')}`
}
const toHtml = (data) => Object.entries(transform(data)).map(d => toTemplate(...d)).join('\n\n')
console.log(toHtml(data))
This is one liner simple solution, it will work.
const data = [{
name: "Thor",
universe: "Marvel",
type: "God"
},
{
name: "Batman",
universe: "DC",
type: "Human"
},
{
name: "Iron man",
universe: "Marvel",
type: "Human"
},
];
let obj = {
Human: 1,
God: 2
}
console.log(data.sort((a, b) => obj[a.type] - obj[b.type]))

How to use drag and drop in ant design?

What I want is an example about how to make the drag and drop of my Table that works properly, but I cannot figure out how to make it works in (functional components)
My code :
function Preview() {
const { name } = useParams();
const [fieldsOfForm, setFieldsOfForm] = useState([]);
const [selectedForm, setSelectedForm] = useState([]);
const columns = [
{
title: 'Posição',
dataIndex: 'pos',
width: 30,
className: 'drag-visible',
render: () =>
<MenuOutlined style={{ cursor: 'grab', color: '#999' }}/>
},
{
title: "Form Name",
dataIndex: "field",
key: "field",
render: (text) => <a>{text}</a>,
},
{
title: "Tipo",
dataIndex: "fieldtype",
key: "fieldtype",
},
];
useEffect(() => {
let mounted = true;
let loadingStates = loading;
if (mounted) {
setFieldsOfForm(location.state);
loadingStates.allFields = false;
setLoading(false);
}
return () => (mounted = false);
}, [selectedForm]);
return (
//Some jsx....
<Row gutter={1}>
<Col span={1}></Col>
<Table dataSource={fieldsOfForm}
columns= {columns}/>
</Row>
// More jsx...
);
}
export default Preview;
Everything that I found on internet about this drag and drop from the lib of antd is in class component , but I wanted to make it works in my functional one.
Example that I found :
multi row drag-able table (antd)
I want some example in function component if someone has tried it already and could help me ?
Here's a functional working example:
import React from "react";
import "antd/dist/antd.css";
import { Table } from "antd";
import {
sortableContainer,
sortableElement,
sortableHandle
} from "react-sortable-hoc";
import { MenuOutlined } from "#ant-design/icons";
const data = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
index: 0
},
{
key: "2",
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
index: 1
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 2
},
{
key: "4",
name: "4",
age: 32,
address: "New York No. 1 Lake Park",
index: 3
},
{
key: "5",
name: "5",
age: 42,
address: "London No. 1 Lake Park",
index: 4
},
{
key: "6",
name: "6",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 5
}
];
const DragHandle = sortableHandle(({ active }) => (
<MenuOutlined style={{ cursor: "grab", color: active ? "blue" : "#999" }} />
));
const SortableItem = sortableElement((props) => <tr {...props} />);
const SortableContainer = sortableContainer((props) => <tbody {...props} />);
function SortableTable() {
const [dataSource, setDataSource] = React.useState(data);
const [selectedItems, setSelectedItems] = React.useState([]);
const getColumns = () => {
return [
{
title: "Sort",
dataIndex: "",
width: 30,
className: "drag-visible",
render: (d, dd, i) => (
<>
<DragHandle active={selectedItems.includes(i)} />
</>
)
},
{
title: "Name",
dataIndex: "name",
className: "drag-visible"
},
{
title: "Age",
dataIndex: "age"
},
{
title: "Address",
dataIndex: "address"
}
];
};
const merge = (a, b, i = 0) => {
let aa = [...a];
return [...a.slice(0, i), ...b, ...aa.slice(i, aa.length)];
};
const onSortEnd = ({ oldIndex, newIndex }) => {
let tempDataSource = dataSource;
if (oldIndex !== newIndex) {
if (!selectedItems.length) {
let movingItem = tempDataSource[oldIndex];
tempDataSource.splice(oldIndex, 1);
tempDataSource = merge(tempDataSource, [movingItem], newIndex);
} else {
let filteredItems = [];
selectedItems.forEach((d) => {
filteredItems.push(tempDataSource[d]);
});
let newData = [];
tempDataSource.forEach((d, i) => {
if (!selectedItems.includes(i)) {
newData.push(d);
}
});
tempDataSource = [...newData];
tempDataSource = merge(tempDataSource, filteredItems, newIndex);
}
setDataSource(tempDataSource);
setSelectedItems([]);
}
};
const DraggableContainer = (props) => (
<SortableContainer
useDragHandle
disableAutoscroll
helperClass="row-dragging"
onSortEnd={onSortEnd}
{...props}
/>
);
const DraggableBodyRow = ({ className, style, ...restProps }) => {
// function findIndex base on Table rowKey props and should always be a right array index
const index = dataSource.findIndex(
(x) => x.index === restProps["data-row-key"]
);
return (
<SortableItem
index={index}
{...restProps}
selected={selectedItems.length}
onClick={(e) => {
if (e.ctrlKey || e.metaKey) {
selectedItems.includes(index)
? selectedItems.splice(selectedItems.indexOf(index), 1)
: selectedItems.push(index);
setSelectedItems(selectedItems);
} else {
setSelectedItems([]);
}
}}
/>
);
};
return (
<>
<h3>"CNTRL + Click" to select multiple items</h3>
<Table
pagination={false}
dataSource={dataSource}
columns={getColumns()}
rowKey="index"
components={{
body: {
wrapper: DraggableContainer,
row: DraggableBodyRow
}
}}
/>
{selectedItems.length ? <>{selectedItems.length} items selected </> : ""}
</>
);
}
You can play with it in Sandbox

Filter an array of objects based on multiple search terms

Say I have a search term, const searchTerms = ['sarasota', 'fl']
And a list of locations,
const selectedRestaurants = [{
city: "Orlando",
name: "Dons Food Place",
state: "FL",
id: 1
},
{
city: "Sarasota",
name: "Rons World",
state: "FL",
id: 2
}]
If I search 'Sarasota FL', I want to only return the one Sarasota FL location. But if I'm searching based on multiple parameters (City and State) the search returns both locations, because it matches the state during my second iteration.
EDIT: I also need this to work when I search only 'Sarasota'
Here is my code:
filterBySearchTerm(searchTerms) {
let searchedRestaurants = [];
for (var j = 0; j < selectedRestaurants.length; j++) {
searchTerms.forEach(term => {
if (term.toLowerCase() === this.state.selectedRestaurants[j].city.toLowerCase()) {
searchedRestaurants.push(this.state.selectedRestaurants[j])
}
if (term.toLowerCase() === this.state.selectedRestaurants[j].state.toLowerCase()) {
searchedRestaurants.push(this.state.selectedRestaurants[j])
}
if (term.toLowerCase() === this.state.selectedRestaurants[j].name.toLowerCase()) {
searchedRestaurants.push(this.state.selectedRestaurants[j])
}
})
}
//I use Set to remove duplicates ^^
searchedRestaurants = [...new Set(searchedRestaurants)]
return searchedRestaurants;
}
I've been stuck on this for a bit now...can anyone help?
Might not make a difference but I'm in ReactJS.
const restaurants = [
{
city: 'Orlando',
name: 'Dons Food Place',
state: 'FL',
id: 1,
},
{
city: 'Sarasota',
name: 'Rons World',
state: 'FL',
id: 2,
},
];
const searchTerms = ['sarasota', 'fl'];
const lowerCasedTerms = searchTerms.map((term) => term.toLowerCase());
const params = ['city', 'state'];
const result = restaurants.filter((restaurant) =>
lowerCasedTerms.every((lowerCasedTerm) =>
params.some((param) => restaurant[param].toLowerCase().includes(lowerCasedTerm)),
),
);
console.log(result);
const selectedRestaurants = [
{
city: "Orlando",
name: "Dons Food Place",
state: "FL",
id: 1,
},
{
city: "Sarasota",
name: "Rons World",
state: "FL",
id: 2,
},
{
city: "Sarasota",
name: "Rons World",
state: "SL",
id: 3,
},
];
function search(searchTerms) {
let isCity = !!selectedRestaurants.filter((i) =>
searchTerms.includes(i.city.toLowerCase())
).length;
let isState = !!selectedRestaurants.filter((i) =>
searchTerms.includes(i.state.toLowerCase())
).length;
return selectedRestaurants.filter((r) => {
if (isCity && isState) {
return (
searchTerms.includes(r.city.toLowerCase()) &&
searchTerms.includes(r.state.toLowerCase())
);
} else {
return searchTerms.includes(r.city.toLowerCase());
}
});
}
// All city match with sarasota
console.log(search( ["sarasota"]));
// All city match with sarasota and state f1
console.log(search( ["sarasota", "fl"]));
What do you think about this approach? It's merging all object values to check if all search terms are included inside it.
const selectedRestaurants = [
{
city: "Orlando",
name: "Dons Food Place",
state: "FL",
id: 1
},
{
city: "Sarasota",
name: "Rons World",
state: "FL",
id: 2
}
];
const searchTerms = ['sarasota', 'fl'];
const foundRestaurants = selectedRestaurants.filter((restaurant) => {
const valuesAsString = Object.values(restaurant).join(' ').toLowerCase();
const allTermsMatching = searchTerms
.map(term => valuesAsString.includes(term.toLowerCase()))
.every(match => !!match);
return allTermsMatching;
});
console.log(foundRestaurants);

Categories

Resources