Show highest score from list - javascript

I have an array in this.state. Array has objects having 2 keys: [text] and [score].
I need to display [text] key only of object with highest score.
e.g.
[{
text: 'foo',
score: 1,
},
{
text: 'bar',
score: 0.1,
}]
Here highest score is 1 with [text] key value as "foo".
So just render "foo".
Code:
class Score extends React.Component {
constructor(props) {
super(props);
this.state =
{
text: [
{
title: 'el',
data: [
{
el: 'hello',
score: 1
},
{
el: 'hallo',
score: 0.10
}
]
},
]
}
}
render() {
return (
<div>
{
this.state.text.map((q, i) => (
<div key={i} className="card">
{
q.data.map((i, j) =>
(
<div key={j}>
<p>{i.el}</p>
<p>{i.score}</p>
</div>
)
)}
</div>
))
}
</div>
);
}
}
const Root = document.getElementById("root");
ReactDOM.render(<Score />, Root);

Array#map will display every element, not just that one with highest score.
Inside map you should drop some logic which will find the object with highest score and display it.
this.state.text.map((q, i) => {
const r = q.data.sort((a, b) => b.score - a.score)[0];
// ^^^^^^^^^^^^^^^^^^^ sort ascending and pick the first element
return (
<div key={i} className="card">
<p>{r.el}</p>
<p>{r.score}</p>
</div>
);
})
const r = [{ text: 'hello', score: 1 }, { text: 'hallo', score: 0.1 }];
const res = r.sort((a, b) => b.score - a.score)[0];
console.log(res);

you can use reduce and find the highest before rendering.
const maxObject = this.state.text.reduce(function(prev, current) {
return (prev.score > current.score) ? prev : current
},this.state.text[0])
and then you can use this maxObject in your jsx.

you need to sort the values, but your structure is such that you need to search inside the first object of your text array...
relevant js:
import React from "react";
class Score extends React.Component {
constructor(props) {
super(props);
const dataForArray = [
{
title: "el",
data: [
{
el: "hello",
score: 1
},
{
el: "hallo",
score: 1.1
},
{
el: "hallo",
score: 0.1
}
]
}
];
let dataToSort = dataForArray[0].data;
if (dataToSort) {
const highestVal = dataToSort.sort((a, b) => b.score - a.score )[0];
console.log("highest:", highestVal);
}
this.state = { text: dataForArray, highest:highestVal };
}
render() {
return (
<div>
{this.state.text.map((q, i) => (
<div key={i} className="card">
{q.data.map((i, j) => (
<div key={j}>
<p>{i.el}</p>
<p>{i.score}</p>
</div>
))}
</div>
))}
<hr/>
Highest value is: <b> {this.state.highest.el} - {this.state.highest.score} </b>
</div>
);
}
}
export default Score;
complete working stackblitz here

Related

Simple animation with useTransition in react-spring

I have a very simple example about useTransition, my expectation is whenever i click on the shuffle button, the items below swap around by a smooth animation. But i doesn't work, the item does swapping but also the pos property. I think my understand about key in useTransition has something wrong, but i can't find it.
my current code: https://codesandbox.io/s/wonderful-solomon-c0sve?file=/src/index.jsx
what im trying to do is something like this
function App() {
const [items, setState] = useState([
{ name: 'C' },
{ name: 'D' },
{ name: 'E' },
{ name: 'F' },
{ name: 'G' },
{ name: 'A' },
{ name: 'B' },
]);
let index = -1;
const gridItems = items.map((item) => {
index += 1;
return { ...item, pos: index * 60 };
});
const transitions = useTransition(gridItems, item => item.name, {
from: () => ({ pos: -100 }),
enter: ({ pos }) => ({ pos }),
udpate: ({ pos }) => ({ pos }),
leave: () => ({ pos: -100 }),
});
return (
<div>
This is app<br/>
<button onClick={ () => setState(Lodash.shuffle) }>shuffle</button><br/><br/>
<div>
{transitions.map(({ item, props, key }) => {
return (
<animated.div
key={key}
className="item"
style={{ transform: props.pos.interpolate(pos => `translateY(${pos}px)`) }}
>
{`${item.name}`}
</animated.div>
)
})}
</div>
</div>
)
}
It was an age to figuring it out. You made a typo.
Try with this one:
update: ({ pos }) => ({ pos }),

React map over the array object

I'm quite new with react stuff, what I am trying is to generate some dynamic stuff using .map()
This is my component:
import React, { Component } from "react";
class DynamicStuff extends Component {
state = {
options: [
{ id: 1, optionOne: "OptionOne" },
{ id: 2, optionTwo: "OptionTwo" },
{ id: 3, optionThree: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option) => {
return {option}
})}
<span>{options.optionOne}</span>
<span>{options.optionTwo}</span>
<span>{options.optionThree}</span>
</>
);
}
}
export default DynamicStuff;
What I am doing wrong here and why the map is not generating expected result ?
Is it ok?
import React, { Component } from "react";
class DynamicStuff extends Component {
state = {
options: [
{ id: 1, value: "OptionOne" },
{ id: 2, value: "OptionTwo" },
{ id: 3, value: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option) => {
return <span>{option.value}</span>
})}
</>
);
}
}
export default DynamicStuff;
You have made your options object incorrectly. We need to have a same attribute over all the objects in the array.
class App extends React.Component {
state = {
options: [
{ id: 1, option: "OptionOne" },
{ id: 2, option: "OptionTwo" },
{ id: 3, option: "OptionThree" }
]
};
render() {
const options = [...this.state.options];
return (
<>
{options.map((option, index) => (
<li key={index}>{option.option}</li>
))}
</>
);
}
}
Another thing, If you need to map an array. You don't need to have many spans. Having a one is just enough. The map function will iterate and give you all the things.
The map used here is actually to convert the js object into a react element, but your usage here is still a js object after the map conversion. The react element may be a <p key = {option.id}> {option. optionOne} </p>.
If there is a react element after the return in your map, it is correct.
{options.map((option) => {
return <p key = {option.id}> {option. optionOne} </p>
})}
or
{options.map((option) => <p key = {option.id}> {option. optionOne} </p>)}
YOu need to map and return the HTML element
return ({
options.map((option) => {
return `<span key={option.id}>${option. option}</span>`
})
});
You should do something like
render() {
const { options } = this.state;
return (
<div className="option-wrapper">
{options.length > 0 && options.map(option => {
return <span key={option.id}>{option.option}</span>
})}
</div>
);
}

How to sort Array Data in ReactJs

class LifeCycleComps extends Component {
constructor(props) {
super(props);
this.state = {
data: 0,
names: [{
name: "sam"
},
{
name: "hammer"
},
{
name: "jellyfish"
}
]
};
//below is sortAlphabet function
sortAlphabet = () => {
this.setState({
names: this.state.names.sort()
});
};
//sortNames component
class SortNames extends Component {
render() {
return <span > {
this.props.names.name
} < /span>;
}
}
<button onClick={this.sortAlphabet}>sort</button>
<ul>
{this.state.names.map((item, i) => (
<SortNames key={i} names={item} /> ))}
</ul>
Above is my code. I am not sure what is the main problem. In the above code I want to get sorted names by onClick. But I am not getting any positive results from the above snippet. Please let me know folks what I did wrong?
You can not directly use sort function in array of object. For that you need to write a sort function or write a callback function which you can modify according your need. Here is working code(https://stackblitz.com/edit/react-31un7h) :
import React, { Component } from 'react';
import { render } from 'react-dom';
const SortNames = (props) => {
return (
<span >
{props.names.name}
</span>
)
}
class LifeCycleComps extends Component {
constructor(props) {
super(props);
this.state = {
data: 0,
names: [{
name: "sam"
},
{
name: "hammer"
},
{
name: "jellyfish"
}
]
};
}
compare = ( a, b ) => {
if ( a.name < b.name ){
return -1;
}
if ( a.name > b.name ){
return 1;
}
return 0;
}
//below is sortAlphabet function
sortAlphabet = () => {
this.setState({
names: this.state.names.sort(this.compare)
});
};
render(){
return (
<div>
<button onClick={this.sortAlphabet}>sort</button>
<ul>
{this.state.names.map((item, i) => (
<SortNames key={i} names={item} /> ))}
</ul>
</div>
);
}
}
//sortNames component
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div>
<LifeCycleComps/>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Here is sorted values
let arr = [{
name: "sam"
},
{
name: "hammer"
},
{
name: "jellyfish"
}]
function sortIt(x,y) {
if ( x.name < y.name ){
return -1;
}
if ( x.name > y.name ){
return 1;
}
return 0;
}
arr.sort(sortIt);
console.log(arr);
And here is in reactjs
sortIt(x,y) {
if ( x.name < y.name ){
return -1;
}
if ( x.name > y.name ){
return 1;
}
return 0;
}
sortAlphabet = () => {
this.state.names.sort(this.sortIt)
this.setState({
names: this.state.names.sort()
});
};
render() {
return (
<>
<button onClick={this.sortAlphabet}>sort</button>
<ul>
{this.state.names.map((item, i) => (
<li key={i} names={item}>{item.name}</li> ))}
</ul>
</>
);
Its to just adapt the javascript code provided in the comments as below.
compare = ( a, b ) => {
if ( a.name < b.name ){
return -1;
}
if ( a.name > b.name ){
return 1;
}
return 0;
}
//below is sortAlphabet function
sortAlphabet = () => {
let objs = [...this.state.names] //create a copy here as you will not want to directly mutate the state by calling sort.
this.setState({
names: objs.sort( compare );
});
};
I've made an StackBlitz in which you could see the solution. Hope this helps.

Updating nested objects in nested arrays in React

Expected effect:
click button -> call function save -> pass object p to function update
update second object{a: 'purple', desc: 'grt', date: '12 -10-2019 '} in colors array, which is in theproducts array
Before update: {a: 'purple', desc: 'grt', date: '12 -10-2019 '}
After update: {a: 'violet', desc: 'gt', date: '12 -12-1980 '}
Error in console.log:
Uncaught TypeError: this.props.product.colors.map is not a function
App
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}]
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a: 'white', {a:'black', desc: 'grtrt', date: '12-12-2119'}, }, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0 //It is first object in products array
index: 1 //It is second object in colors array
}
}
update = (item) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: item}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
Items
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
</div>
);
}
}
Item
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
You need to pass the index of the colors item and then update it accordingly
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
and then in the topmost parent
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorsIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
Working demo
const { Component } = React;
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}],
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a:'black', desc: 'grtrt', date: '12-12-2119'}, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0,
index: 1
}
}
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
}
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
);
}
}
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<pre>{JSON.stringify(this.props.time)}</pre>
<button onClick={this.save}>Save</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app" />

React: How to update object in array map

I'm trying to update the value of an array when a button is clicked. But I can't figure out how to do so using this.setState.
export default class App extends React.Component {
state = {
counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
};
render() {
return this.state.counters.map((counter, i) => {
return (
<div>
{counter.name}, {counter.value}
<button onClick={/* increment counter.value here */}>+</button>
</div>
);
});
}
}
How do I increment counter.value when the button is clicked?
Update
Since this is the accepted answer now, let me give another optimal method after #Auskennfuchs' comment. Here we use Object.assign and index to update the current counter. With this method, we are avoiding to use unnecessary map over counters.
class App extends React.Component {
state = {
counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
};
increment = e => {
const {
target: {
dataset: { i }
}
} = e;
const { counters } = this.state;
const newCounters = Object.assign(counters, {
...counters,
[i]: { ...counters[i], value: counters[i].value + 1 }
});
this.setState({ counters: newCounters });
};
render() {
return this.state.counters.map((counter, i) => {
return (
<div>
{counter.name}, {counter.value}
{/* We are using function reference here */}
<button data-i={i} onClick={this.increment}>
+
</button>
</div>
);
});
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
As an alternative method, you can use counter name, map over the counters and increment the matched one.
class App extends React.Component {
state = {
counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
};
increment = name => {
const newCounters = this.state.counters.map(counter => {
// Does not match, so return the counter without changing.
if (counter.name !== name) return counter;
// Else (means match) return a new counter but change only the value
return { ...counter, value: counter.value + 1};
});
this.setState({counters: newCounters});
};
render() {
return this.state.counters.map((counter, i) => {
return (
<div>
{counter.name}, {counter.value}
<button onClick={() => this.increment(counter.name)}>+</button>
</div>
);
});
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
If you use the handler like above it will be recreated on every render. You can either extract it to a separate component or use datasets.
class App extends React.Component {
state = {
counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
};
increment = e => {
const {target} = e;
const newCounters = this.state.counters.map(counter => {
if (counter.name !== target.dataset.name) return counter;
return { ...counter, value: counter.value + 1};
});
this.setState({counters: newCounters});
};
render() {
return this.state.counters.map((counter, i) => {
return (
<div>
{counter.name}, {counter.value}
{ /* We are using function reference here */ }
<button data-name={counter.name} onClick={this.increment}>+</button>
</div>
);
});
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
Consider refactoring the actual counter into its own component. That simplifies the state management as it encapsulates the component responsibility. Suddenly you don't need to update a nested array of objects, but you update just a single state property:
class App extends React.Component {
state = {
counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
};
render() {
return this.state.counters.map((counter, i) => {
return (
<Counter name={counter.name} count={counter.value} />
);
});
}
}
class Counter extends React.Component {
state = {
count: this.props.count
}
increment = () => {
this.setState({
count: this.state.count+1
})
}
render() {
return (
<div>
{this.props.name}, {this.state.count}
<button onClick={this.increment}>+</button>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You could use the i that you already have to map the counters to a new array, replacing the target item with an updated count:
() => this.setState(({ counters }) => ({ counters: counters.map((prev, i2) => i === i2 ? { ...counter, count: counter.count + 1 } : prev) }))
You can have an handler that will map counters and update the corresponding counters item of the button clicked. Here we take i from the parent scope, and compare it to find the right item to change.
<button onClick={() => {
this.setState(state => ({
counters: state.counters.map((item, j) => {
// is this the counter that I want to update?
if (j === i) {
return {
...item,
value: item.value + 1
}
}
return item
})
}))
}}>+</button>
You can create a click handler like this
handleClick = (index) => {
this.setState(state => {
const obj = state.counters[index]; // assign the object at the index to a variable
obj.value++; // increment the value in the object
state.counters.splice(index, 1); // remove the object from the array
return { counters: [...state.counters, obj] };
});
}
Call it like this... <button onClick={() => handleClick(i)}>
It's possible to make this shorter. Just wanted to explain how you could go about it
With Array.splice you can replace an entry inside of an array. This will return a new array with the replaced value:
const {counters}= this.state
return counters.map((counter, i) => (
<div key={i}>
{counter.name}, {counter.value}
<button onClick={() => this.setState({
counters: counters.splice(i, 1, {
...counter,
value: counter.value+1,
}
)})}>+</button>
</div>
));
Also it's best practise to give every Fragment inside of a loop it's own unique key. And you can get rid of the return, because there's only the return inside of the map function.

Categories

Resources