There must be an easy answer to this but I can't seem to find one.
JSON: -
{
1: {
'desc' : 'desc1',
'title' : 'title 1',
},
2: {
'desc' : 'desc2',
'title' : 'title 2',
},
3: {
'desc' : 'desc3',
'title' : 'title 3',
},
4: {
'desc' : 'desc4',
'title' : 'title 4',
},
5: {
'desc' : 'desc5',
'title' : 'title 5',
},
6: {
'desc' : 'desc6',
'title' : 'title 6',
},
};
So basically I want to iterate this object and have every 3 items nest in a .row class div like this: -
<div class='row'>
<div>desc1 : title1</div>
<div>desc2 : title2</div>
<div>desc3 : title3</div>
</div>
<div class='row'>
<div>desc4 : title4</div>
<div>desc5 : title5</div>
<div>desc6 : title6</div>
</div>
Now I understand how I can simple render() this in a component: -
{
Object.keys(JSONObject).map(key => <TheComponent key={key} details={JSONObject[key]} />)
}
However, I am stumped as to how I can split this into 2 rows of 3 items and nest each of the 3 items in the .row div.
Any help greatly appreciated. I am relatively new to ES6/ReactJS and eager to get past the teething problems.
One way of looking at this is to split the array up first into chunks, then working with that.
So you could get your initial array;
var ar1 = Object.keys(JSONObject);
Then chunk it up
var ar2 = [];
var i, j, chunk = 3;
for (i = 0, j = ar1.length; i < j; i += chunk) {
ar2.push(ar1.slice(i, i+chunk));
}
This will then give you ar2 which is an array of arrays. You could then either loop through this and output the level one array with the rows, then the level 2 array with the inner html.
See this basic bit of code on jsbin to demo the chunking
let arrTheComponent = [], arrJson = [];
Object.keys(JSONObject).map(key => {
arrJson.push(JSONObject[key]);
if (arrJson.length === 3) {
arrTheComponent.push(<TheComponent json={arrJson} />);
arrJson = [];
}
})
Display the component
export const TheComponentList = () => {
let arrTheComponent = [], arrJson = [];
Object.keys(JSONObject).map(key => {
arrJson.push(JSONObject[key]);
if (arrJson.length === 3) {
arrTheComponent.push(<TheComponent json={arrJson} />);
arrJson = [];
}
})
return (<div>
{arrTheComponent}
</div>);
}
I did something very similar, so here is the code I used:
let rowCount = 0;
let rows = [];
Object.keys(JSONObject).forEach((k, i) => {
// this just makes sure there is always an array initialised
rows[rowCount] = rows[rowCount] || [];
// now we add into the current row a component
rows[rowCount].push(
<TheComponent
key={k}
details={JSONObject[key]}
/>
);
if ((i + 1) % 3 === 0) { // the 3 here is how many items you want per row!!
// if we can perfectly divide the current position by 3
// we have filled a row, so now increment the row
rowCount++;
}
});
This will give you an array of arrays:
[
[ <TheComponent key=1 />, <TheComponent key=2 /> <TheComponent key=3 /> ]
[ <TheComponent key=4 /> ... ]
[ .... ]
]
Then you can render like this:
render() {
const rows = this.groupRows(); // this is the function above
// ar below is a single array of <TheComponent ... /> so looks like:
// [ <TheComponent ... />, ... ]
return (
<div>
{ rows.map((ar, i) => <div className="row"> { ar } </div>) }
</div>
);
}
Addendum
I actually created a component to do this for me, so I could use it like so:
render() {
return (
<Grouper>
{ Object.keys(JSONObject).map(k => <TheComponent key={k} details={ JSONObject[key] } />) }
</Grouper>
);
}
And what the <Grouper /> component did was React.Children.forEach(...) grouping them using the code I have above. Which meant all that component scaffolding code was nicely abstracted away from the business view logic.
Related
`import React from 'react'
export default function Quiz(props){
// generate random index without duplicates
function generateRandomIndex(){
const randomNumArr=[]
for (var a = [0, 1, 2, 3], i = a.length; i--; ) {
var random = a.splice(Math.floor(Math.random() * (i + 1)), 1)[0];
randomNumArr.push(random)
}
return randomNumArr
}
let randomNumbers = generateRandomIndex()
let spreadOptions = ()=>{
let optionsHtmlArray = []
for(let i=0; i<props.answers.length; i++){
optionsHtmlArray.push(`<span className='answers' key=${i} style={${{backgroundColor: props.correct===props.answers[i] ? "green" : "red"}}}>
{ ${props.answers[i]} } </span>`)
}
return optionsHtmlArray
}
return (
<div className='Quiz'>
<h3 className='question'>{props.question}</h3>
<div className='answers_div'>
{ spreadOptions()[randomNumbers[0]] }
{ spreadOptions()[randomNumbers[1]] }
{ spreadOptions()[randomNumbers[2]] }
{ spreadOptions()[randomNumbers[3]] }
</div>
<hr className='hr'/>
</div>)
}
'
'//this is from App.js
// fetch to API when first render to save data to the state,
// and fetch depending on the sate of showOverlay
React.useEffect(() => {
fetch("https://opentdb.com/api.php?amount=5&category=9&difficulty=easy&type=multiple")
.then(res => res.json())
.then(data => {
setQuestions(data.results)
//after set questions state that comes from fetch request
//and set the custom questions with some properties I need
setCustomQuestions(prevQuestions=>{
let newArr=[]
for(let i=0; i<data.results.length; i++){
newArr.push({question: data.results[i].question,
questionId: nanoId(),
answers: [data.results[i].correct_answer].concat(data.results[i].incorrect_answers),
correct: data.results[i].correct_answer})
}
return newArr
})
})
}, [])
// Quiz component properties
const customQuestionsArr = customQuestions.map(question => {
return < Quiz
key={question.questionId}
question={question.question}
answers={question.answers}
correct={question.correct}
/>
})'
Hi all, I am trying to render all options of the answers in Quiz component, however,
spreadOptions() returns an array of html strings for the answers
I gotta parse to JSX to make it work.
I tried to install react-html-parser, didn't work it only gave me a bunch of error every time when I try to install dependencies through npm
I tried dangerouslySetInnerHTML, but also didn't work
Would you be able to provide the props that you are trying to pass to Quiz component?
Below is a snippet of code with modified spreadOptions and jsx. I wasn't able to test this code tho but will update it if you can provide the sample props.
let spreadOptions = props.answers.map((a, i) => (
<span
key={i}
className='answers'
style={{
backgroundColor: props.correct === a ? 'green' : 'red',
}}
>
{a}
</span>
));
return (
<div className="Quiz">
<h3 className="question">{props.question}</h3>
<div className="answers_div">
{spreadOptions}
</div>
<hr className="hr" />
</div>
);
I have a JSON file and I am trying to calculate the JSON file key based on the value and reformating it. My JSON file looks like below:
data=[
{
pet:'Cat',
fruit:'Apple',
fish:'Hilsha'
},
{
pet:'Dog',
fish:'Carp'
},
{
pet:'Cat',
fruit:'Orange',
fish:'Lobster'
}
];
I do like to calculate and formate it like below:
data=[
{
label:'Pet',
total:3,
list:[
{
name:'Cat',
value: 2,
},
{
name:'Dog',
value: 1,
}
]
},
{
label:'Fruit',
total:2,
list:[
{
name:'Apple',
value: 1,
},
{
name:'Orange',
value: 1,
}
]
},
{
label:'Fish',
total:3,
list:[
{
name:'Hilsha',
value: 1,
},
{
name:'Carp',
value: 1,
},
{
name:'Lobster',
value: 1,
}
]
},
];
If anybody can help me, it will be very help for me and will save a day.
I have fixed this task myself. If I have any wrong, you can put your comment fill-free :)
``
ngOnInit(): void {
this.dataService.$data.subscribe(data => {
// Create new object and calculation according to category
let petObj: any = {}
let fruitObj: any = {}
let fishObj: any = {}
data.forEach((el: any) => {
if (el.pet != undefined) {
petObj[el.pet] = (petObj[el.pet] || 0) + 1;
}
if (el.fruit != undefined) {
fruitObj[el.fruit] = (fruitObj[el.fruit] || 0) + 1;
}
if (el.fish != undefined) {
fishObj[el.fish] = (fishObj[el.fish] || 0) + 1;
}
});
// Create list according to category
let pet_list: any = [];
let fruit_list: any = [];
let fish_list: any = [];
for (var key in petObj) {
let pet = {
label: key,
value: petObj[key]
}
pet_list.push(pet)
}
for (var key in fruitObj) {
let fruit = {
label: key,
value: fruitObj[key]
}
fruit_list.push(fruit)
}
for (var key in fishObj) {
let fish = {
label: key,
value: fishObj[key]
}
fish_list.push(fish)
}
// Calculate total sum according to category
var totalPet = pet_list.map((res: any) => res.value).reduce((a: any, b: any) => a + b);
var totalFruit = fruit_list.map((res: any) => res.value).reduce((a: any, b: any) => a + b);
var totalFish = fish_list.map((res: any) => res.value).reduce((a: any, b: any) => a + b);
// Rearrange the JSON
this.rearrangeData = [
{
label: 'Pet',
total: totalPet,
list: pet_list
},
{
label: 'Fruit',
total: totalFruit,
list: fruit_list
},
{
label: 'Fish',
total: totalFish,
list: fish_list
}
]
console.log(this.rearrangeData)
// End rearrange the JSON
});
}
``
You can simplify your function. Take a look this one
group(oldData) {
const data = []; //declare an empty array
oldData.forEach((x) => {
//x will be {pet: 'Cat',fruit: 'Apple',fish: 'Hilsha'},
// {pet: 'Dog',fish: 'Carp'}
// ...
Object.keys(x).forEach((key) => {
//key will be 'pet','fruit',...
const item = data.find((d) => d.label == key); //search in the "data array"
if (item) { //if find it
item.total++; //add 1 to the property total of the element find it
// and search in the item.list the 'Cat'
const list = item.list.find((l) => l.name == x[key]);
//if find it add 1 to the property value of the list
if (list)
list.value++;
else
//if not, add to the list
//an object with property "name" and "value" equal 1
item.list.push({ name: x[key], value: 1 });
} else
//if the element is not in the "array data"
//add an object with properties label, total and list
//see that list is an array with an unique element
data.push({
label: key,
total: 1,
list: [{ name: x[key], value: 1 }],
});
});
});
return data;
}
You can use like
this.dataService.$data.subscribe(data => {
this.rearrangeData=this.group(data)
}
NOTE: this function the labels are 'pet','fruit' and 'fish' not 'Pet', 'Fruit' and 'Fish'
Did you try reading the text leading up to this exercise? That'd be my first approach. After that, I'd use reduce. You can do pretty much anything with reduce.
Looking to group and create relevant html based from an object.
const _ = require('lodash');
const items = [
{
value: 'fruit',
label: 'apple',
},
{
value: 'Mango',
label: 'Mango',
groupBy: 'fruit'
},
{
value: 'Orange',
label: 'Orange',
groupBy: 'fruit'
},
// Will need to group all above by fruit, similarly
{
value: 'vegetable',
label: 'artichoke',
},
{
value: 'aubergine',
label: 'aubergine',
groupBy: 'vegetable'
}
];
_renderItems = () => {
const itemsList = _.chain(items)
.map(item => (
this._renderItem(item)
))
.value()
return '<div class="item-container">'+ itemsList+'</div>'
}
_renderItem = (item = {}) => {
console.log(item)
}
_renderItems()
/*
desired output
<div class="fruit">
Label:Apple
Label:Mango
Label:Orange
</div>
<div class="vegetable">
label:artichoke
label:aubergine
label:broccoli
</div>
*/
Code sample here of progress here https://repl.it/repls/ElectronicUsableTheories . In general, I have trouble adding a wrapping div based on grouped value.
So all fruit should be grouped first key will not have groupBy key but its value will be the key of all next items which needs to be grouped
Group the items by the groupBy or by value if groupBy doesn't exist. Then you can map the groups. The 2nd parameter that map passes to the callback is the key (the groupBy value), that you can use as the class. It addition map the items, take the label, and format. Combine the group's string, and the itemList's string, and return.
const items = [{"value":"fruit","label":"apple"},{"value":"Mango","label":"Mango","groupBy":"fruit"},{"value":"Orange","label":"Orange","groupBy":"fruit"},{"value":"vegetable","label":"artichoke"},{"value":"aubergine","label":"aubergine","groupBy":"vegetable"}];
const _renderItem = ({ label } = {}) => `label: ${label}\n`;
const _renderItems = () =>
_(items)
.groupBy(o => o.groupBy || o.value) // if not groupBy use value
.map((group, key) => {
const itemsList = group.map(_renderItem).join('');
return `<div class="item-container ${key}">\n${itemsList}</div>`;
})
.join('\n');
const result = _renderItems();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
I am developing an application with React and I have a problem. I have made a fetch from a json and the values are repeated. I want only each value to appear once.
I hope that it only returns once each of the elements of "Padre": "CRM", "Permisos" and "Telefonia". The same for "Hijo".
I've tried changing ".concat" to ".reduce" because I've read that it can be done like this, but it does not work. I have seen examples but none of them works on "this.state" so I do not know how to do it.
Can you help me with the correct way to do it?
This is a part of my json file
[
{
"Id":"114",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"115",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"116",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"44",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"45",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"46",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"47",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"50",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"51",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"52",
"Padre":"Telefonia",
"Hijo":"Configuracion"
},
{
"Id":"70",
"Padre":"Telefonia",
"Hijo":"Rutas"
}
]
For confidential reasons I can not show the actual file.
In my next code I change the fetch for a fake json url
I Edit my code with #Avanthika and #blaz code. Currently the code works but shows only the first value it finds of "Father" and "Son". I need to show only once each of the different values of "Father" and "Son". Example result:
Father:
CRM
PERMISOS
Son:
ARGUMENTARIOS
ROOT
Currenly, with my new code i see only: CRM (Padre) and Argumentarios (Son). The rest of the elements are not shown.
import React, { Component } from 'react';
class Nav extends Component{
constructor(props){
super(props)
this.state = {
menuSubmenu:[]
}
}
componentWillMount(){
fetch('http://FAKE.php')
.then(response => response.json())
.then(menuSubmenu =>{
menuSubmenu.forEach(datos => {
let data = {
menu:datos.Padre,
submenu:datos.Hijo,
id:datos.Id
}
//console.log( data )
//console.log (data.menu);
this.setState({
menuSubmenu:this.state.menuSubmenu.concat([data])
})
})
})
}
render() {
const array = [...this.state.menuSubmenu];
const distinctValues = array.filter(
({ Padre, Hijo }, index) => {
return array.findIndex(item => item.Padre === Padre && item.Hijo === Hijo) === index;
});
//console.log(this.state.menuSubmenu);
//console.log(distinctValues);
if (distinctValues.length > 0) {
return(
<div>
{distinctValues.map(datos => <Navegacion key={datos.id} menu={datos.menu} submenu={datos.submenu} />)}
</div>
);
}
return (<p>Cargando usuarios...</p>);
}
}
class Navegacion extends Component{
render(){
return (
<ul className="list">
<li className="list__item">{this.props.menu}
<ul className="list">
<li className="list__item">
{this.props.submenu}
</li>
</ul>
</li>
</ul>
)
}
}
export default Nav;
This image is the result of my json.My json result
I hope you can help me with the "reduce" function.
¡Thanks a lot!
Hi you can use lodash and function uniqBy https://lodash.com/docs#uniqBy
_.uniqBy(menuSubmenu, function (e) {
return e.Padre;
});
You can use new Set to unique your array list by Padre and Hijo.
const array = [{"Id":"114","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"115","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"116","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"44","Padre":"Permisos","Hijo":"root"},
{"Id":"45","Padre":"Permisos","Hijo":"root"},
{"Id":"46","Padre":"Permisos","Hijo":"root"},
{"Id":"47","Padre":"Permisos","Hijo":"root"},
{"Id":"50","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"51","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"52","Padre":"Telefonia","Hijo":"Configuracion"},
{"Id":"70","Padre":"Telefonia","Hijo":"Rutas"}];
const distinctValues = Array.from(new Set(array.map(elem => `${elem.Padre}-${elem.Hijo}`))).map(distinctVal => {
const [ Padre, Hijo ] = distinctVal.split("-");
return ({
Id: array.find(elem => elem.Padre === Padre && elem.Hijo === Hijo).Id,
Padre,
Hijo
});
});
console.log(distinctValues);
Or you can go for lodash as mentioned to extract uniq values. You have to use uniqBy.
const array = [{"Id":"114","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"115","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"116","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"44","Padre":"Permisos","Hijo":"root"},
{"Id":"45","Padre":"Permisos","Hijo":"root"},
{"Id":"46","Padre":"Permisos","Hijo":"root"},
{"Id":"47","Padre":"Permisos","Hijo":"root"},
{"Id":"50","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"51","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"52","Padre":"Telefonia","Hijo":"Configuracion"},
{"Id":"70","Padre":"Telefonia","Hijo":"Rutas"}];
const distintValues = _.uniqBy(array, elem => [elem.Padre, elem.Padre].join());
console.log(distintValues);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You need to fix the solution this way:
render() {
const array = [...this.state.menuSubmenu];
// Put the solution you like here.
// const distinctValues = the solution you like
if (distinctValues.length > 0) {
return(
<div>
{distinctValues.map(datos => <Navegacion key={datos.id} menu={datos.menu} submenu={datos.submenu} />)}
</div>
);
}
return (<p>Cargando usuarios...</p>);
}
Original solution from Avanthika doesn't work because in your data, some item might possibly have Padre value containing dash character ("-"). Use a character that will never appear in Padre value as separator instead and you will be good.
My solution:
Use Array.prototype.filter
const distinctValues = this.state.menuSubmenu.filter(
({ Padre, Hijo }, index) => {
return this.state.menuSubmenu.findIndex(item => item.Padre === Padre && item.Hijo === Hijo) === index;
});
With Array.prototype.reduce it will look more bulky:
const distinctValues = this.state.menuSubmenu.reduce(
(array, item) => {
if (array.findIndex(i => i.Padre === item.Padre && i.Hijo === item.Hijo) === -1) {
array.push(item);
}
}, []);
I want to make a cool higher-order function chain for what I could do (perhaps more verbosely) like this:
for (var idx = 0; idx < collecionA.length; idx++) {
for (item in collectionA[idx].children) {
if (item.sku == "someVal") return idx
}
}
Does anyone see a snazzy way to do this with map/find/filter/reduce etc.? I keep wanting to use forEach but then get pwnd when I realize I can't return from it.
Something like:
return collectionA.children.findIndex( (child) => child.children.oneOfThemIncludesAnObjectWithThisProperty("someVal"))
Use Array.findIndex() on the outer collection. For each item, iterate the children with Array.some(), and check if the value of the property (sku) matches the requested value. As soon as a matching value is found, some returns true immediately, and findIndex returns the current index.
const collection = [{"children":[{"sku":"someOtherVal"}]},{"children":[{"sku":"someVal"}]},{"children":[{"sku":"someOtherVal"}]}];
const findIndexWithChildProp = (arr, prop, val) =>
arr.findIndex(({ children }) =>
children.some(({ [prop]: v }) => v === val));
const result = findIndexWithChildProp(collection, 'sku', 'someVal');
console.log(result);
This could be what you are looking for:
function func() {
var index = -1;
collectionA.forEach((p, i) => p.children.forEach(item => {
if (item.sku == "someVal") index = i;
}));
return index;
}
var collectionA = [{
children: [{
sku: "someOtherVal"
}]
}, {
children: [{
sku: "someVal"
}]
}, {
children: [{
sku: "someOtherVal"
}]
}]
console.log(func());