Here is my array. How can I remove duplicates in this type of structure? When I map over arr I get the values of each array nested in each object. And I want to filter the duplicated values.
current output: bye hello hello
The expected output should be: bye hello
[
{
arr: ['']
val: "string"
}
{
arr: ['bye', 'hello']
val: "string"
}
{
arr: ['hello']
val: "string"
}
]
myArray.map(item => item.arr.map((el, index) =>
<p key={index}>{el}</p>
))
I hope it will help you:
const filteredArray = useMemo(() => {
const used = []
return myArray.map(sub => {
return { ...sub, arr:sub.arr.map(el
=> {
if(used.includes(el) return null
used.push(el)
return el
}}
})
}, deps)
And then in JSX:
filteredArray.map(() => ...)
You could simply manage an array to filter what you want to display :
import React from 'react';
import { render } from 'react-dom';
import './style.css';
const App = () => {
const data = [
{
arr: [''],
val: 'string',
},
{
arr: ['bye', 'hello'],
val: 'string',
},
{
arr: ['hello'],
val: 'string',
},
];
const buildData = () => {
const passedValues = [];
return data.map((item) => {
return item.arr.map((el) => {
if (!passedValues.includes(el)) {
passedValues.push(el);
return <div>{el}</div>;
}
});
});
};
return <div>{buildData()}</div>;
};
render(<App />, document.getElementById('root'));
Here is the repro on StackBlitz.
All of these answer are good...I think #vitaliyirtlach has the best as its the closest to React.
I'll just put it out there that you can also loop through myArray, remove the duplicates with Set and then place them in an array that you can loop over:
const myArray = [
{
arr: [''],
val: "string"
},
{
arr: ['bye', 'hello'],
val: "string"
},
{
arr: ['hello'],
val: "string"
}
]
const removeDupes = () => {
const newArr = []
myArray.map(item => item.arr.map((el, index) =>
newArr.push(el)
))
return [...new Set(newArr)]
}
const loopArray = removeDupes();
console.log(loopArray)// logs ["","bye","hello"]
loopArray.map((el, index) =>
<p key={index}>{el}</p>
))
Related
I'm trying to render out the lowest value of three values in an array. It looks like this
const compareValues = [
companyResult.physicalAverage,
companyResult.stressAverage,
companyResult.developmentAverage,
];
const getLowestValue = (Math.min(...compareValues)); // returns 4.5 (which is correct)
return (
<div>
<p>{getLowestValue}</p> // HERE I want to render out the name (string) instead of 4.5
</div>
);
However, I want to name each property, i.e
companyResult.physicalAverage = "Physical Activity"
companyResult.stressAverage = "Stress",
companyResult.developmentAverage = "Personal Development"
I still want to return the lowest value with Math.min, but somehow return the 4.5 as a name. Anyone that has a clue on how?
Get the min value yourself.
const compareValues = [
{name: 'physicalAverage', value: companyResult.physicalAverage},
{name: 'stressAverage', value: companyResult.stressAverage},
{name: 'developmentAverage', value: companyResult.developmentAverage},
];
let minObj = compareValues[0]
compareValues.forEach(obj => {
if(obj.value < minObj.value)
minObj = obj;
})
return (
<div>
<p>{minObj.name}</p>
</div>
);
You can use useMemo hooks to apply sort function. But you will have to tweak your compareValues array a bit to include details about title.
const {useMemo} = React;
const companyResult = {
physicalAverage: 4.5,
stressAverage: 5,
developmentAverage: 6
};
function App() {
const compareValues = useMemo(
() => [
{ value: companyResult.physicalAverage, title: "Physical Activity" },
{ value: companyResult.stressAverage, title: "Stress" },
{ value: companyResult.developmentAverage, title: "Personal Development" }
],
[]
);
const lowest = useMemo(() => {
return compareValues.sort((a, b) => a.value < b.value)[0];
}, [compareValues]);
return (
<div>
<p>Label: {lowest.title}</p>
<p>Value: {lowest.value.toString()}</p>
</div>
);
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<div id="root"></div>
If these are the only values in the object, you can make it even simpler by using
const compareValues = Object.entries(companyResult);
const minValue = compareValues.reduce((acc, entry) => entry[1] > acc[1] ? acc : value);
// minValue[0] is the key
// minValue[1] is the value
You need to change your data structure. And then create a custom function (findMinValue) to find the minimum value in your data.
const values = [
{ name: "Physical Activity", value: companyResult.physicalAverage },
{ name: "Stress", value: companyResult.stressAverage },
{ name: "Personal Development", value: companyResult.developmentAverage },
];
const findMinValue = (array) =>
array.reduce((prev, curr) => (prev.value < curr.value ? prev : curr));
const lowestValue = findMinValue(values);
return (
<div>
<p>{lowestValue.name}</p>
</div>
);
I'm trying to fill an object with values that I'm getting from an array of objects but it's not working as expected.
This is a simplified code example
https://codesandbox.io/s/crazy-nobel-c7xdb?file=/src/App.js
import "./styles.css";
import React, { useEffect, useState } from "react";
export default function App() {
const [fieldsValues, setFieldsValues] = useState({});
const items = [{ value: "a" }, { value: "b" }, { value: "c" }];
useEffect(() => {
items.map((item, index) => {
return setFieldsValues({
...fieldsValues,
[index]: item.value
});
});
}, []);
return (
<div className="App">
<h2> {` fieldsValues = ${JSON.stringify(fieldsValues)}`} </h2>
</div>
);
}
I want the fieldsValues to return this:
{
0: "a",
1: "b",
2: "c"
}
What I'm getting now:
fieldsValues = {"2":"c"}
You fix it by doing this
useEffect(() => {
items.map((item, index) => {
return setFieldsValues((prev) => ({
...prev,
[index]: item.value,
}));
});
}, []);
Better way of doing this is
useEffect(() => {
const data = items.reduce(
(prev, item, index) => ({ ...prev, [index]: item.value }),
{}
);
setFieldsValues((prev) => ({ ...prev, ...data }));
}, []);
To create the object map the array to [index, value] pairs, and convert to an object with Object.fromEntries():
const items = [{ value: "a" }, { value: "b" }, { value: "c" }];
const result = Object.fromEntries(items.map(({ value }, index) => [index, value]))
console.log(result)
However, the way you are using the array, and then need to set the state doesn't actually makes sense in the react context.
If the array is a prop, you should add it to useEffect as a dependency:
const arrToObj = items => Object.fromEntries(items.map(({ value }, index) => [index, value]))
export default function App({ items }) {
const [fieldsValues, setFieldsValues] = useState({});
useEffect(() => {
setState(() => arrToObj(items))
}, [items]);
...
If it's a static array, set it as the initial value of setState:
const arrToObj = items => Object.fromEntries(items.map(({ value }, index) => [index, value]))
const items = [{ value: "a" }, { value: "b" }, { value: "c" }];
export default function App({ items }) {
const [fieldsValues, setFieldsValues] = useState(() => arrToObj(items));
...
By your way It would be like this
useEffect(() => {
let test={}
items.map((item, index) => {
return setFieldsValues((prev)=>{
return {
...prev,
[index]: item.value
}
});
});
}, []);
I have a MenuOptions component that I pass an options prop to. Options is a large array of objects. Each object has a nested array called 'services' inside services is an object with a key 'serviceType' which is the only value I want. I want to take all those values, push them into a new array and remove any duplicates if there are any, and then map through that new array and display each item in an 'option' html tag.
here is my createArray function:
const createNewArray = () => {
let optionsArr = []
let uniqueArr;
options.map((option) => {
option.services.map((service) => optionsArr.push(service.serviceType))
})
uniqueArr = [...new Set(optionsArr)];
return uniqueArr;
}
uniqArr seems to be holding what I want but now I want to set this to a piece of global state. Trying something like this does not work. array seems to still be set as null
const [array, setArray] = useState(null)
useEffect(() => {
setArray(createNewArray())
}, [])
any solutions? Thanks
1) You should add your array state initial value as an empty array:
const [array, setArray] = useState([]);
Live Demo
2) You can simplify the creating of a new array as:
const createNewArray = () => [
...new Set(options.flatMap((o) => o.services.map((obj) => obj.serviceType)))
];
3) set array state in useEffect as:
useEffect(() => {
setArray(createNewArray());
}, []);
From your description is this your data?
const options = [{
services: [
{
serviceType: 'serviceType',
}
],
},{
services: [
{
serviceType: 'serviceType',
}
],
},{
services: [
{
serviceType: 'serviceType',
}
],
},
{
services: [
{
serviceType: 'serviceType',
}
],
}]
here is the solution
const uniq = (a) => [...new Set(a)];
const createNewArray = (array) => {
const c = [...array]
const newArray = []
for (let i = 0; i < c.length; i++) {
const e = c[i];
for (let ii = 0; ii < e.length; ii++) {
const ee = e[ii].serviceType;
newArray.push(ee);
}
}
const toReturn = uniq(newArray)
return toReturn;
}
If you want unique options, just pass the options in and set them to the state after you massage the data.
const { useEffect, useMemo, useState } = React;
const unique = (arr) => [...new Set(arr)];
const uniqueOptions = (options) =>
unique(options.flatMap(({ services }) =>
services.map(({ serviceType }) => serviceType)));
const data = {
options: [
{ services: [{ serviceType: "Home" } , { serviceType: "About" }] },
{ services: [{ serviceType: "About" } , { serviceType: "Help" }] },
{ services: [{ serviceType: "Help" } , { serviceType: "Home" }] },
],
};
const MenuOptions = (props) => {
const { options } = props;
const [opts, setOpts] = useState([]);
useEffect(() => setOpts(uniqueOptions(options)), [options]);
return useMemo(
() => (
<select>
{opts.map((opt) => (
<option key={opt} value={opt}>
{opt}
</option>
))}
</select>
),
[opts]
);
};
const App = ({ title }) =>
useMemo(
() => (
<div>
<h1>Services</h1>
<form>
<MenuOptions options={data.options} />
</form>
</div>
),
[]
);
ReactDOM.render(<App />, document.getElementById("react-app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react-app"></div>
I'm trying to use the filter() method to filter out items from an array, if the array's length is less than a certain number, in Reactjs. I haven't been able to accomplish this so far.
Code Sample:
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = {
itemNumber: 2,
items: [
{ text: "Item 1" },
{ text: "Item 2" },
{ text: "Item 3" },
{ text: "Item 4" }
]
}
}
filterItem = () => {
if ( this.state.items.length > this.state.itemNumber ) {
console.log('Items length is higher');
let newItems = this.state.items.filter(item => {
return item < this.state.itemNumber;
});
console.log(newItems);
} else {
console.log('Items length is lower');
}
};
render() {
const { items } = this.state;
return (
<div>
<ul>
{ this.state.items.map(item =>
(
<li key={item.id}>
{ item.text }
</li>
)
)}
</ul>
<button onClick={this.filterItem}>Filter items!</button>
</div>
)
}
}
Edit:
Solution here.
You're never setting the state
filterItem = () => {
if ( this.state.items.length > this.state.itemNumber ) {
console.log('Items length is higher');
let newItems = this.state.items.filter((item,i) => {
return i < this.state.itemNumber;
});
this.setState({items: newItems})
console.log(newItems);
} else {
console.log('Items length is lower');
}
};
I'm not exactly sure what you're trying to do but can't you just use slice?
const newItems = [...this.state.items].slice(0, this.state.itemNumber).map(item =>
Just as #Dupocas said, you never update your state.
You need to use this.setState to update React component's state.
I think you should have 2 kinds of array in your states
this.state = {
itemNumber: 2,
items: [
{ text: "Item 1" },
{ text: "Item 2" },
{ text: "Item 3" },
{ text: "Item 4" }
],
filteredItems: [],
}
Then you should modify the filter function like this:
filterItem = () => {
if ( this.state.items.length > this.state.itemNumber ) {
console.log('Items length is higher');
let newItems = this.state.items.filter((item, index) => {
return index < this.state.itemNumber;
});
console.log(newItems);
this.setState({filteredItems: newItems});
} else {
console.log('Items length is lower');
}
}
Notice the this.setState({filteredItems: newItems}); line, this line will update this.state.filteredItems.
Finally, in the render function:
render() {
const { filteredItems } = this.state;
return (
<div>
<ul>
{ filteredItems.map(item =>
(
<li key={item.id}>
{ item.text }
</li>
)
)}
</ul>
<button onClick={this.filterItem}>Filter items!</button>
</div>
)
}
This will render an empty array until the filter button is clicked.
To display the complete array on first time render, add this function to the component
componentDidMount() {
this.setState({filteredItems: [ ...this.state.items ]});
}
In case somebody is asking what does the [ ...this.state.items ] do, it's called array destructuring.
use filter to filter an array,
let arr = ["item1", "item12", "item123"];
arr = arr.filter((item) => item.length >= 6)); //6 is the min length of item that you want.
It will return the new array with the filtered result.
You can declare any other array and store the result in that also
like
let arr = ["item1", "item12", "item123"];
let arr1 = arr.filter((item) => item.length >= 6));
so I have a reducer that is adding to array
create reducer :
export default (itemsList = [], action) => {
if (action.type === 'ADD_ITEM') {
return [...itemsList, action.payload]
}
return itemList
}
deleting reducer (99% that something is wrong here, but I have no idea what ):
export default (itemList = [], action) => {
if (action.type === 'DELETE_ITEM') {
return [...itemList, itemList.filter(item => item !== action.payload)]
}
return itemList
};
action/index.js:
export const addItemToList = item => {
return {
type: 'ADD_ITEM',
payload: selectedItem
}
};
export const deleteItemFromList = item => {
return{
type: 'DELETE_ITEM',
payload: selectedItem
}
};
let say I have
itemList = [ 'abc', 'xyz', 'qwe' ]
and I want to use deleteItem('xyz') to delete 'xyz' from itemList
While deleting you just need to return the filtered list and not use spread operator too.
export default (itemList = [], action) => {
if (action.type === 'DELETE_AUTHOR') {
return itemList.filter(item => item !== action.payload)
}
return listOfAuthorsSelected
};
Array.filter() returns a new array with given filter condition and not mutate the existing array. You don't have need to use ...itemList(spread operator). Here you are actually adding a sub array each time.
Here is a simple running example
var array1 = ["abc", "def" , "ghi"];
var array2 = [...array1, array1.filter((item) => {
return item !== "def"
})];
document.write(array2);
// correct way to filter
var array3 = ["abc", "def" , "ghi"];
var array4 = array3.filter((item) => {
return item !== "def"
});
document.write("<hr/>"+array4);