Rendering nested array object values into JSX element - arrays

I have a nested array of objects that looks something like this:
[
{
_Category: "Animals",
_Child: {
_Obj: [{_Title: "Monkey", _Link: "www.monkeys.com"}], [{_Title: "Monkey", _Link: "www.monkeys.com"}], etc
}
},
{
_Category: "Fruit",
_Child: {
_Obj: [{_Title: "Apple", _Link: "www.apples.com"}], [{_Title: "Pineapple", _Link: "www.pineapples.com"}], etc
}
}
]
I'm trying to append the _Title and _Link of each group into a JSX element. I've been able to render the _Categories without a problem, but the nested values are giving me a hard time.
When I go to the Elements in DevTools, I can see that there are 11 different groups for each item---which is how many array of objects there are under that particular category---but the _Titles and _Links are empty.
In the code below, I mapped through the _Category data and displayed the Categories that way. Do I have to do something similar for the _Child object---another map, maybe, or something else?
Code:
Element = ({ cats }) => {
return (
<div>
{cats
.filter((cat) => cat._Root === "Quick Links")
.map((cat) => (
<>
<div className={"colFor-" + cat._Category}>
<h5>{cat._Category}</h5>
{cat._Child._Obj.map((item) => (
<>
<a href={item._Link}>
<p>{item._Title}</p>
</a>
<br />
</>
))}
</div>
</>
))
}
</div>
)
}
Here's what cats looks like:
Update: As #Dominik pointed out, the way my obj was set up was incorrect so I changed it.

According to your sample data, cat._Child._Obj is an array and each item in it is also an array, so I think you need to flat you array first then use the map function.
const myarr = [[{_Title: "Monkey1", _Link: "www.monkeys1.com"}], [{_Title: "Monkey2", _Link: "www.monkeys1.com"}]];
const myarrFlat = myarr.flat();
myarrFlat.map(cat => console.log(cat._Title, cat._Link));

Related

Search function from JSON structure

I am looking for a solution to search JSON values based on the user input in the search field.
For example: if the user keys in ASD, any data that contain ASD in the Json values should display.
I found an example which works like how i wanted it to be. However, they are not using JSON structure.
Here's the code that I have followed:
App.svelte:
<script>
import Search from "./Search.svelte";
const data = [
"Metrics Server",
"Node.js",
"Virtual Machine",
"Virtual Private Server",
];
</script>
<Search autofocus {data} let:filtered>
<ul>
{#each filtered as item}
<li>{item}</li>
{/each}
</ul>
</Search>
Search.svelte
<script>
import {onMount} from "svelte";
export let autofocus = false;
export let data = [];
let input = undefined;
let value ="";
onMount(() =>{
if(autofocus) {
input.focus();
}
});
$: filtered = data.filter((item) =>
item.toLowerCase().includes(value.toLowerCase()));
</script>
<style>
ul{
list-style:none;
}
</style>
<input bind:this="{input}" type="search" bind:value />
<ul>
{#each filtered as item}
<li>{item}</li>
{/each}
</ul>
This code does not work for JSON structure such as:
{"name": "tommy, "class":"a"},
{"name": "dummy, "class":"b"} ...
It will return an error like:
item.toLowerCase is not a function
How do I implement a search function that will return me the name if the user search for "tommy"
This is how i am retrieving the Json data:
let info ="";
onMount(async () =>{
const resp = await fetch('https:xx.xxx.xxx')
info = await resp.json();
});
And the data I am getting back is in this format: [editted]
[ {
"table_schema": "x",
"table_name": "a",
"column_name": "typname",
"data_type": "name",
"character_maximum_length": null
},
{
"table_schema": "b",
"table_name": "x",
"column_name": "typnamespace",
"data_type": "oid",
"character_maximum_length": null
}]
I have edited my real JSON file. The code does works for my dummy JSON but not the real one. Any idea?
You would have to loop through each of the properties on your items and compare each of them:
first loop over the items with data.filter
get all the props of each item Object.keys(item)
check if at least one matches a condition .some
compare the key in the item to the value item[key].toLower....
$: filtered = data.filter(item =>
Object.keys(item).some(key =>
item[key].toLowerCase().includes(value.toLowerCase())
)
);
Note that this code assumes all items in your array are objects, it will fail if you have a mix between objects and strings:
[ "Tommy", { name: "Sammy" }]
Here the first element doesn't really have 'keys'. You would have to add an additional check for that first.

How to filter an object for display based on key names in React?

I have a state object that includes multiple address lines
{
name: "fred",
homeStreet1: "1 main st"
homeStreet2: "Apt A"
}
How can I iterate over the address lines to display them in React?
I tried:
{state.filter(entry => { entry.key.match(/homeStreet/) &&
<tr key={entry.value}>
<td><label htmlFor={entry.key}/>
<input
type="text"
id={`homeStreet${entry.value + 1}`}
value={entry.value}
size="20"
onChange={(e) => updateState(e.target.id, e.target.value )} />
</td>
</tr >;
})}
but I get
Expected an assignment or function call and instead saw an expression no-unused-expressions
Object.entries (doc) would fulfill your way of thinking with object's key-value pairs
The Object.entries() method returns an array of a given object's own enumerable string-keyed property [key, value] pairs
const obj = {
name: 'fred',
homeStreet1: '1 main st',
homeStreet2: 'Apt A'
}
Object.entries(obj).map(([key, value]) => {
if (key.match(/homeStreet/)) {
console.log(key, value)
// Do what you want then..
}
})
Supposing that all the addresses lines are home<something> you could do something like:
let state = {
name: "fred",
homeStreet1: "1 main st",
homeStreet2: "Apt A"
}
let filtered_keys = (obj, filter) => {
let key, keys = []
for (key in obj)
if (obj.hasOwnProperty(key) && filter.test(key))
keys.push(key)
return keys
}
let filteredAddress = filtered_keys(state, /home/)
console.log(filteredAddress)
filteredAddress.forEach(el => {
console.log(state[el])
})

Group by Data Field

I have a list component contains SUBJECT, NO, DESCRIPTION fields. For example: ACC 121, descr.., ACC 121 descr..., ACC 122 descr... ACC 211 desc.... How do I group by the same SUBJECT and NO in tag and DESCRIPTION in sub components?
Courses=[{id:1, SUBJECT: ACC, NO:121,DESCR: 'class description1'},
{id:2, SUBJECT:ACC, NO:121, DESCR:'class description2'},
...]
const CourseList=({Courses, onCourseSelect})=>{
const renderedList= Courses.map(course=>{
return (
//add <h2>{course.SUBJECT} {course.NO} ???
<CourseItem key={course.ID} descr={course.DESCR} onCourseSelect={onCourseSelect} ></CourseItem>
);
})
return <div className="List ui relaxed divided list">
{renderedList}</div>
}
-----EDITED CODE
var _ = require('lodash');
const CourseList=({Courses, onCourseSelect})=>{
let renderedList = _.groupBy(Courses, 'SUBJECT','NO');
console.log(renderedList);
object list like below:
ACC: (26) […]
0: Object { ID: 1079, DESCR: "class description1", … }
1: Object { ID: 1080, DESCR: "class description2", … }
2: Object { ID: 1081, DESCR: "class description3",...}
​​
How to render each object? I tried:
return <div>
{Object.keys(renderedList).map(key => (
{renderedList["SUBJECT"]}
</div>
but it does not work. Thanks.
The Idea is to do something like this. Create a helper function to restructure the data into a multidimensional array by grouping them so that each array in the multidimensional array holds records about courses with the same id. then loop through the multidimensional array and perform whatever logic you want to.
let courses=[{id:1, SUBJECT: "ACC", NO:121,DESCR: 'class description1'},
{id:2, SUBJECT:"ACC", NO:121, DESCR:'class description2'},
...]
//Where ever we want to print the first group we just need to call the CourseList with a single input from the multidimentional structured array asonst
CourseList=(courses, onCourseSelect)=>{
//create another variable to hold the structured data
//this ensures that each key in the array is it self another array holding all the courses with that same id.
let structuredData = new Array();
courses.map(course => structuredData[course.id] = (courses.filter(co =>(co.id == course.id ))))
<div className="List ui relaxed divided list">
structuredData.map((data, k) => {
//each iteration over data is printing for a single group
console.log("Group", k)
data.map(course=>
<CourseItem key={course.id} descr={course.DESCR} onCourseSelect={onCourseSelect} />
)
})
</div>
}

How to create the sectiondata in the react native sectionlist

I want show the data from my API use Sectionlist in the react native
but I have no idea how to generate the sectionData.
I think my data is too complex to understand and let me confuse.
The struct like below
"Info": [
{
"Name": "test1",
"Data": [
{
"sss": "1215",
"aaa": "1010133000001",
},
{
"sss": "1215",
"aaa": "1010133000001",
}
]
},
{
"Name": "test2",
"Data": [
{
"sss": "1215",
"aaa": "1010133000001",
},
{
"sss": "1215",
"aaa": "1010133000001",
}
]
}
]
}
I want show the SectionHeader use Nameand show the Contents use Data.
Now I can get data use potion of code like below. How can I go on? Thank you!
for (let idx in jsonData) {
let Item = jsonData[idx];
console.log(Item.Name)
for (let index in Item.Data) {
Item2 = Item.Data[index];
console.log(Item2.sss)
}
}
First, you have to update your data array,
let newArray = []
for (let item in jsonData) {
let dict = {
title: item.Name,
data: item.Data
}
newArray.push(dict)
}
After the above code, you will get the array for section list.
Then use this like the following,
...
<SectionList
renderItem={({item, index, section}) => <Text key={index}>{item.sss}</Text>}
renderSectionHeader={({section: {title}}) => (
<Text style={{fontWeight: 'bold'}}>{title}</Text>
)}
sections={this.state.data}
keyExtractor={(item, index) => item + index}
/>
...
The 'this.state.data' is your new array.
And renderItem is used for rendering section data
And renderSectionHeader is used for rendering section header.
provide an array of items, this will spit out the section data
// maps it's an array of objects
// groupBy to extract section headers
let dataSource = _.groupBy(maps, o => o.name);
// reduce to generate new array
dataSource = _.reduce(dataSource, (acc, next, index) => {
acc.push({
key: index,
data: next
});
return acc;
}, []);
return dataSource;

Why .map, .length is not working on array, when it's printing an array

It's printing an array in console but when I use .map or .length, it prints undefined or gives error that .map is not a function.
The code is inside the render method:
let { faqs } = this.props;
console.log(faqs); // prints array
console.log(this.props.faqs.length) // prints undefined
let questionList = faqs.length > 0 ? faqs.map((faq) => {
return <div key={faq._id}>
<p className='mb-0'>Question: {faq.question}</p>
<p className='mb-0'>Answer: {faq.answer}</p>
</div>
}) : <div> no questions found.</div>
This is the console output:
{faqs: Array(0)}
undefined
{faqs: Array(5)}
faqs: Array(5)
0: {_id: "5cb44201cdd6fe2749c7ced5", question: "question.1", answer: "this.is.an.answer", __v: 0}
1: {_id: "5cb44255cdd6fe2749c7ced6", question: "sadsfghjkl;", answer: "dsfghjkl;'↵", __v: 0}
2: {_id: "5cb451dbcdd6fe2749c7ced7", question: "dscsfghjk", answer: "sdfghjkl", __v: 0}
3: {_id: "5cb4553ccdd6fe2749c7ced8", question: "weeweqw", answer: "dsadasdsad", __v: 0}
4: {_id: "5cb457b2cdd6fe2749c7ced9", question: "q.5.", answer: "answer.undefined", __v: 0}
undefined
According to your logs, it looks like faqs is an object containing an array named faqs.
Maybe this is a mistake and you want to pass an array directly instead of an object containing an array.
<MyComp faqs={[...]} />
If so, const { faqs } = this.props would give you the array and your code would work.
However, if you pass the data as <MyComp faqs={{ faqs: [...] }} />, then you can access the inner array via a desctructuring assignment like this:
const { faqs: { faqs } } = this.props;
This will fix the errors you are having with .length returning undefined and .map is not a function.
Then you would continue like this, first checking if faqs is truthy before calling .length or .map:
const questionList = faqs && faqs.length ? faqs.map(faq => { ...
Note that you can also assign default values in the destructuring assignment so the array is always properly defined and truthy, which avoids polluting the code with && checks everywhere:
const { faqs: { faqs = [] } = {} } = this.props;
Then you can directly access .length and .map:
const questionList = faqs.length ? faqs.map(faq => {
return <div key={faq._id}>
<p className='mb-0'>Question: {faq.question}</p>
<p className='mb-0'>Answer: {faq.answer}</p>
</div>
}) : <div> no questions found.</div>
In your case initially there is no values for faqs. .map & .length only works on array. Thats why it returns undefined
so you have to do an if condition on faqs
if(faqs)
console.log(faqs.length)
And
faqs && faqs.map(item => return {
.......
} )
by the console logs, it seems like prop faqs is an object with key faqs which is the array, so in your code do faqs.faqs.length or map

Resources