Problem:
I have an api and each object within the api doesn't have a value. I would like to add a unique value to each object within the array so that I can create a function and use 'e.target.value' with event listeners. I'm doing this in nextjs.
Why:
Because I want to store each value in to an array and localstorage before eventually displaying the data that was stored in the array as like a favorites item.
Is there a way of doing this ?
Information:
data.items has over 30+ objects -
"items": [
{
"id": 119603782,
"node_id": "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
"name": "react-contextual",
"full_name": "drcmda/react-contextual",
"private": false,
"owner": {
"login": "drcmda",
"id": 2223602,
}
{
"id": 119603782,
"node_id": "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
"name": "react-contextual",
"full_name": "drcmda/react-contextual",
"private": false,
"owner": {
"login": "drcmda",
"id": 2223602,
}
So far my data has been sorted and mapped like so.
{data.items
.sort(function (a, b) {
return b.stargazers_count - a.stargazers_count && new Date (b.created_at) - new Date(a.created_at)
})
.map((d) => (
<div onClick={checkId} key={d.id} className=" border-white p-5">
<h1 className="text-2xl font-bold">Repo name: {d.name}</h1>
An example using index as unique for displaying purpose.
const data = {
items: [
{
id: 119603782,
node_id: "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
name: "react-contextual",
full_name: "drcmda/react-contextual",
private: false,
owner: {
login: "drcmda",
id: 2223602,
},
},
{
id: 119603782,
node_id: "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
name: "react-contextual",
full_name: "drcmda/react-contextual",
private: false,
owner: {
login: "drcmda",
id: 2223602,
},
},
],
};
const items = data.items.map((item, idx) => ({ ...item, idx }));
// new item
const newItem = {
id: 119603782,
node_id: "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
name: "react-contextual",
full_name: "drcmda/react-contextual",
private: false,
owner: {
login: "drcmda",
id: 2223602,
},
};
function addNewItems(items, newItem) {
newItem.idx = items.length;
items.push(newItem);
return items;
}
console.log(addNewItems(items, newItem));
If you want add a VALUE key to each object you can do the following:
const [isLoading, setIsLoading] = useState(false),
[items, setItems] = useState([]);
useEffect(() => {
(async () => {
setIsLoading(true);
try {
//you can replace axios with fetch
const res = await axios('https://your-api'),
// clone deep is from lodash => you can use the spread (...) operator if you want
clonedItems = cloneDeep(res.data.items);
clonedItems.forEach((el) => {
// add whatever you want in the (value)
el.value = 'required value';
});
//sort items based on the required key
clonedItems.sort((a, b) => {
//replace name with your key
if (a.name.toLowerCase() < b.name.toLowerCase()) {
return -1;
}
if (a.name.toLowerCase() > b.name.toLowerCase()) {
return 1;
}
return 0;
});
//check the modified items
console.log(clonedItems);
setItems(clonedItems);
} catch (err) {
//replace it with your error handler code
console.log(err);
} finally {
setIsLoading(false);
}
})();
}, []);
Notes:
You should sort your elements before storing it in the state
You can replace axios with fetch
you can use the spread operator (...) in place of cloneDeep from lodash
Related
anyone has an idea what causes the ff issue ? I cannot insert new object to a key object in my arrays of objects for example I wanna insert new email to emails at index 1 when I push to that it causes error cannot add property 1 , onject is not extensible in .
Ideads and help would be much appreciated. Thank.s
#code
const addEmail = (id: number) => {
console.log('...RegionalList',RegionalList)
const regionalList = [...RegionalList];
const index = regionalList
.map((prop: IRegionalList) => prop.id)
.indexOf(id);
console.log('regionalList[index].emails' , regionalList[index].emails)
regionalList[index].emails.push({
emailAddress: '',
firstName: '',
lastName: '',
id: Math.floor(Math.random() * 999),
fetching: false,
});
setRegionalList(regionalList);
};
#object where I am inserting on regionalist arrays of object the value of this variable const regionalList = [...RegionalList];
[
{
"id": 4,
"name": "Associate Director of Construction Ops",
"column": "associateDirectorofConstructionOps",
"emails": [
{
"id": 79,
"emailAddress": "crawform#raw.com",
"firstName": "James",
"lastName": "Crawford"
}
]
},
{
"id": 5,
"name": "CAM Manager",
"column": "camManager",
"emails": [
{
"id": 77,
"emailAddress": "jenn.jones4#test.com",
"firstName": "Jennifer",
"lastName": "Jones"
}
]
},
]
#another snippet
const setEmailValue = (event: any, regionalId: number, index: number) => {
setRegionalList((prevState: IRegionalList[]) => {
const newState = prevState.map((prop: IRegionalList) => {
if (prop.id === regionalId) {
prop.emails[index] = { emailAddress: event.target.value, id: null };
return { ...prop };
}
return prop;
});
return newState;
});
}
#another snippet
useEffect(() => {
if (selectedRow) {
console.log('selectedRow' , selectedRow)
// Set selected row data
setData({
regionName: selectedRow['regionName'],
marketName: selectedRow['marketName'],
subRegionName: selectedRow['subRegionName'],
});
let regional = [...RegionalList];
for (const k in selectedRow) {
regional.map((prop: IRegionalList) => {
if (prop.column === k) {
prop.emails = selectedRow[k] ? selectedRow[k] : []
}
})
}
console.log('regional:', regional);
setRegionalList(regional);
}
}, [selectedRow]);
As you cannot mutate the state as in your code above, you have to create and return a new array, so try:
const addEmail = (id: number) => {
setRegionalList(list => list.map(item => {
if (item.id === id) {
return {
...item,
emails: [
...item.emails,
{
emailAddress: '',
firstName: '',
lastName: '',
id: Math.floor(Math.random() * 999),
fetching: false,
}
]
}
}
return item;
}))
};
I am trying to filter a Javascript array of objects with nested objects with specific properties. I can filter the name, slug, website, launch year without any issues. But, I can not filter the category name (category.name) which is an object within the object. Why is filtering the category name not working?
var search = "qui"; // does not work (category.name)
// var search = "Sauer"; // works (name)
var data = [{ "name": "Sauer-Metz", "slug": "ab-laborum",
"website": "https://test.com", "launch_year": 2017, "category_id": 6,
"category": { "id": 6, "name": "qui", "slug": "qui" } } ];
var results = data.filter(company => [
'name', 'launch_year', 'website', 'category.name'
].some(key => String(company[key]).toLowerCase().includes(search.toLowerCase())));
console.log(results);
One way you can go about it is to have a value extractor like the one getKey below
const getKey = (value, key) => {
return key.split('.').reduce((acc, curr) => value[curr], '');
}
var results = data.filter(company => [
'name', 'launch_year', 'website', 'category.name'
].some(key => String(getKey(company, key)).toLowerCase().includes(search.toLowerCase())));
I believe you have to do a separate condition for this specific nested property, although there might be a cleaner way I don't see right now:
var results = data.filter(
(company) =>
["name", "launch_year", "website"].some((key) =>
String(company[key]).toLowerCase().includes(search.toLowerCase())
) ||
String(company["category"]["name"])
.toLowerCase()
.includes(search.toLowerCase())
);
Dot notation doesn't work like that.
const testCase1 = 'qui';
const testCase2 = 'Sauer';
const data = [
{
name: 'Sauer-Metz',
slug: 'ab-laborum',
website: 'https://test.com',
launch_year: 2017,
category_id: 6,
category: { id: 6, name: 'qui', slug: 'qui' },
},
];
const searchResults = (data, search) => {
return data.filter((item) => {
return (
item?.category?.name.toLowerCase().includes(search.toLowerCase()) ||
['name', 'launch_year', 'website'].some((key) => `${item[key]}`.toLowerCase().includes(search.toLowerCase()))
);
});
};
console.log('**CASE 1**')
console.log(searchResults(data, testCase1));
console.log('**CASE 2**')
console.log(searchResults(data, testCase2));
To use your approach you can convert 'category.name' to ['category','name'] and then use String(company[key[0]][key[1]])... whenever key is an array.
const search = "qui"; // does not work (category.name)
//const search = "Sauer"; // works (name)
const data = [{ "name": "Sauer-Metz", "slug": "ab-laborum", "website": "https://test.com", "launch_year": 2017, "category_id": 6, "category": { "id": 6, "name": "qui", "slug": "qui" } } ];
const results = data.filter(
company => [
'name', 'launch_year', 'website', ['category','name']
].some(
key =>
Array.isArray(key) ?
String(company[key[0]][key[1]]).toLowerCase().includes(search.toLowerCase()) :
String(company[key]).toLowerCase().includes(search.toLowerCase())
)
);
console.log(results);
I have a state tree of this form:
const initialState = {
total: 0,
discount: 0,
typeDiscount: 0,
products: data
};
In which products field is an array and the array is like this:
[{
"id":9090,
"name":"Item1",
"price":200,
"discount":10,
"type":"fiction",
"quantity": 1,
"img_url":"https://store.lexisnexis.com.au/__data/media/catalog/thumb//placeholder.jpg"
},
{
"id":9091,
"name":"Item2",
"price":250,
"discount":15,
"type":"literature",
"quantity": 1,
"img_url":"https://store.lexisnexis.com.au/__data/media/catalog/thumb//placeholder.jpg"
}]
Now I'm trying to change the quantity in that array, I'm new to Redux so please guide me on how to do this?
And here is my reducer:
case types.ADD_ITEM_CART:
let product_add = state.products
for (let i = 0; i < product_add.length; i++) {
if (product_add[i].id === action.id) {
product_add[i].quantity = product_add[i].quantity + 1
break
}
}
return dotProp.set(state, `products`, product_add);
You can use Array.prototype.map and iterate over the items and update the item that matches the id from the payload.
Something like that:
const state = [{
"id": 9090,
"name": "Item1",
"price": 200,
"discount": 10,
"type": "fiction",
"quantity": 1,
"img_url": "https://store.lexisnexis.com.au/__data/media/catalog/thumb//placeholder.jpg"
},
{
"id": 9091,
"name": "Item2",
"price": 250,
"discount": 15,
"type": "literature",
"quantity": 1,
"img_url": "https://store.lexisnexis.com.au/__data/media/catalog/thumb//placeholder.jpg"
}
];
const payload = {
id: 9091
};
const nextState = state.map(product => {
if (product.id !== payload.id) {
// not our product, return as is
return product;
}
return {
...product,
quantity: product.quantity + 1
}
});
console.log(nextState);
.as-console-wrapper {
max-height: 100% !important;
}
For removal of items you can just use Array.prototype.filter:
const state = [{
"id": 9090,
"name": "Item1",
"price": 200,
"discount": 10,
"type": "fiction",
"quantity": 1,
"img_url": "https://store.lexisnexis.com.au/__data/media/catalog/thumb//placeholder.jpg"
},
{
"id": 9091,
"name": "Item2",
"price": 250,
"discount": 15,
"type": "literature",
"quantity": 1,
"img_url": "https://store.lexisnexis.com.au/__data/media/catalog/thumb//placeholder.jpg"
}
];
const payload = {
id: 9091
};
const nextState = state.filter(product => product.id !== payload.id);
console.log(nextState);
With the spread operator ... and array.map() you can create a new products array where all the products that do not match the id will keep the same object reference while you create a new product object for you product of interest:
case types.ADD_ITEM_CART:
const updatedProducts = state.products.map(product => {
if (product.id === action.id) {
const newQuantity = product.quantity + 1;
return { ...product, quantity: newQuantity };
}
return product;
})
return { ...state, products: updatedProducts };
}
Edit: Remove can be easily implemented with the array.filter() method, which will create a new array only containing the items that matches your predicate function; in this case all the products that does not match the id of the product you want to remove:
case types.REMOVE_ITEM_CART:
const updatedProducts = state.products.filter(product => product.id !== action.id)
return { ...state, products: updatedProducts };
}
Do not mutate your state like that for Redux or React itself. Copying objects do not create different objects. If you change a property for the new one, you mutate the original one also.
Generally, we use Object.assign or spread syntax combining with methods like .map, .filter. Object.assign or spread syntax does not create totally different objects, too. They create shallow copies, this means one level copy. If you change a nested property for the new object then again you mutate the original one. So, combine all these tools.
case types.ADD_ITEM_CART: {
// We are mapping our related array.
const newProducts = state.products.map( el => {
// If id does not match, return the element without doing nothing.
if ( el.id !== action.id ) { return el };
// id match, increment the quantity.
return { ...el, quantity: el.quantity + 1 };
})
// Lastly, return our state again without mutating it.
return { ...state, products: newProducts };
}
If we want to remove an item from an array we generally use .filter method for this.
const newProducts = state.products.filter( el => el.id !== action.id );
return { ...state, products: newProducts };
I have an object like that:
{
"data": [
{
"id": "1234",
"is_deleted": false,
"name": "Sarah"
},
{
"id": "3520",
"is_deleted": true,
"name": "Bobby"
},
{
"id": "3520",
"is_deleted": true,
"name": "Sartah"
}
]
}
React code
import React from 'react';
import { Input } from 'antd';
import { connect } from 'dva';
const Search = Input.Search;
#connect(({ rule, loading }) => ({
rule,
loading: loading.models.rule,
}))
export default class SearchBox extends React.Component {
constructor(props) {
super(props)
this.state = {
isListLoaded: false,
resultArr: {}
}
}
performSearch(value) {
for( var i = this.props.rule.data.list.length; i--; ) {
for (var key in this.props.rule.data.list[i]) {
this.setState({resultArr: this.state.resultArr.push(i)});
}
}
}
componentDidMount() {
if (!this.state.isListLoaded) {
const { dispatch } = this.props;
dispatch({
type: 'rule/fetch'
});
this.setState({ isListLoaded: true });
}
}
render() {
return (
<div>
<Search
placeholder="Search..."
onChange={(event) => this.performSearch(event.target.value)}
style={{ width: "250px", "margin-left": "20px"}}
/>
</div>
);
}
}
My goal is very simple: I want to search through this object, and
return the entire array(s) that contains the keyword.
Example: if I search "Sar", I should get 2 objects:
{
"id": "1234",
"is_deleted": false,
"name": "Sarah"
},
{
"id": "3520",
"is_deleted": true,
"name": "Sartah"
}
Problem is, I get an error when I'm trying this code. I did search for previous solutions to this problem here on SO, but I can only find examples where there's only one element returned. What I want, is to get ALL the results that contain the keyword in ANY attributes (in this example, I'm returning 2 elements, not just one)
Any idea?
const { data } = {
"data": [
{
"id": "1234",
"is_deleted": false,
"name": "Sarah"
},
{
"id": "3520",
"is_deleted": true,
"name": "Bobby"
},
{
"id": "3520",
"is_deleted": true,
"name": "Sartah"
}
]
};
const keyword = "Sar";
const filtered = data.filter(entry => Object.values(entry).some(val => typeof val === "string" && val.includes(keyword)));
console.log(filtered);
It filters the entries of data Array with the following criterium: at least one of the entry's values must contain a given keyword.
Since IE doesn't yet support Object.values() and String.prototype.includes() you can use the following:
const containsKeyword = val => typeof val === "string" && val.indexOf(keyword) !== -1;
const filtered = data.filter(entry => Object.keys(entry).map(key => entry[key]).some(containsKeyword));
or polyfill these ES6 features, see more here.
To make the keyword lookup case insensitive, you can use RegExp:
const re = new RegExp(keyword, 'i');
const filtered = data.filter(entry => Object.values(entry).some(val => typeof val === "string" && val.match(re)));
Instead of looping through array simply use filter method of javascript
performSearch(value) {
const unfilteredData = this.props.rule.data.list;
const filteredDate = unfilteredData.filter((val) => {
return val.name.indexOf(val) !== -1;
});
this.setState({
resultArr: filteredDate,
})
}
performSearch(value) {
let filteredData = this.props.rule.data.list.filter(item => {
let isFiltered = false;
for(let key in item){
if(item[key].includes(value)){
isFiltered = true;
}
}
return isFiltered;
})
this.setState({resultArr: filteredData});
}
I have data like this:
{
"-L8BpxbS70KYrZMQUF0W": {
"createdAt": "2018-03-22T16:33:57+08:00",
"email": "ss#ss.ss",
"name": "ss"
},
"-KYrZMQUF0WL8BpxbS70": {
// etc.
}
}
Which I want to turn into this:
[{
id: '-L8BpxbS70KYrZMQUF0W
createdAt: "2018-03-22T16:33:57+08:00",
email: "ss#ss.ss",
name: "ss"
}, {
id: -KYrZMQUF0WL8BpxbS70"
// etc.
}]
I'm started with this:
Object.keys(idBasedObjects).forEach(key => {
console.log(resp[key])
})
But I get undefined.
What's best way of creating this array?
Get the keys and values using Object.entries(), and Array.map() them to to the required form using object spread:
const obj = {"-L8BpxbS70KYrZMQUF0W":{"createdAt":"2018-03-22T16:33:57+08:00","email":"ss#ss.ss","name":"ss"},"-KYrZMQUF0WL8BpxbS70":{}};
const result = Object.entries(obj).map(([id, props]) => ({
id,
...props
}));
console.log(result);
Use Object.keys, Object.assign and map
var output = Object.keys(obj).map( s => Object.assign( obj[s], {id : s} ))
Demo
var obj = {
"-L8BpxbS70KYrZMQUF0W": {
"createdAt": "2018-03-22T16:33:57+08:00",
"email": "ss#ss.ss",
"name": "ss"
},
"-KYrZMQUF0WL8BpxbS70": {
"createdAt": "2018-03-22T16:33:57+08:00",
"email": "s2s#ss.ss",
"name": "ss2"
}
};
var output = Object.keys(obj).map(s => Object.assign(obj[s], {
id: s
}));
console.log(output);