Group by Data Field - reactjs

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>
}

Related

React Nested Loop Query

I'm new to React and struggling with something that would be very simple with XML/XPath.
I have two array objects. For simplicity sake, I've removed most properties to demonstrate and just set everything as strings...
customerList: Customer[]
export class Customer {
id: string = "";
firstname: string = "";
}
and then a second array object:
orderList: Order[]
export class Order {
id: string = "";
customerid: string = "";
}
Assume that the order.customerid is nullable.
What I want to do is loop through each customer, and check if there are any customers without an order.
What I've tried:
{customers.map((customer) => {
return(
orders.map((order) => {
if(order.customerid == customer.id)
{
order.customerid == customer.id ? <p>customer has order</p> : <p>customer does not have order</p>
}
})
)
)}
I figured I'd set some sort of boolean flag to indicate whether there are any customers without orders, but just wanted to get something functional.
When you use map functions, a new array is created from what's returned in each loop.
i.e.:
const arr = [1, 2, 3, 4, 5];
const newArr = arr.map((i) => i*i);
console.log(newArr)
//Output: [1, 4, 9, 16, 25]
In your case, since you have not defined what to return, it will just return undefined and hence you will get a nested array of undefined values.
In order to implement the functionality you want, I'd do something like this
const newArr = customerList.map((customer) => {
return {
...customer,
customerOrdered: orders.find(order => order.customerid === customer.id)
}
})
It will create a new array of customers indicating whether the customer has placed an order or not
You not mentioned in the question but i assume that you want to render that the customer to screen that this customer don't have any order.
So you have to RETURN it. this is common mistake when people start using React.
But look like you using typescript and use it wrong way. You don't have to create a class. Create a type or interface instead.
There are many way to archive what you want to do, this just one of them that you won't need to create new array or change state format.
interface Customer {
id: string,
name: string,
}
interface Order {
id: string,
customerId: string
}
// Pretend that you pass customer and orders to this component
function Component({customers, orders}) {
return (
{customers.map((customer) => {
let haveOrder = false;
orders.forEach((order) => {
if(order.customerid == customer.id){
haveOrder = true;
}
});
if (haveOrder){
return(
<p>customer has order</p>
)
}else {
return(
<p>customer does not have order</p>
)
}
)}
)

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.

Rendering nested array object values into JSX element

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));

Infinite loop on JS for

My code stays in the second for forever, testing the same category every step and decrementing every time.
I have two arrays, one of them is called categoriesToUpdate and is a list of category ids (string values) for categories that I have to update, and the other is called categories, containing all the actual category data I'm working with.
I have to test if the id value for a category that I have to update is the same as the database and if it is, decrement the attribute position of its object and update the database. But it is infinitely decrementing the bank.
let newCategory;
let name;
let position;
for(let id of categoriesToUpdate) {
for(let cat of categories) {
if(id === cat.id) {
position = cat.category.category.lastPosition - 1;
name = cat.category.category.categoryName;
newCategory = {
category: {
categoryName: name,
lastPosition: position,
}
}
cRef.child(id).update(newCategory);
}
}
}
Examples of the two arrays:
categoriesToUpdate = ['-lGz4j77...', '-uEbKO3...', ...]
and
categories = [
{
category: {
category: {
categoryName: "name",
lastPosition: "number",
}
},
id: "category id";
},
{
...
}
]
it is difficult to explain how I get the arrays, but basically, categoriesToUpdate is an array of ids that I add to my logic, I have to do update in each of these categoriesand categories is an array that has all categories of the database, comes from Firebase.
let id of categoriesToUpdate. I'm assuming categoriesToUpdate is an array of Category objects. So id is actually a Category object. To compare it should be id.id === cat.id.
Also you can try filter instead of a second loop.
Something like
var matched = categories.filter(c => c.id === id.id)[0];
Then compare matched. Nested loops are hard to read, imo.

Filter in ng-options not working

I have an object that maps IDs to their objects. I would like to display the list of these object and use a filter, but I cannot get it to work. Specifically, I'm trying to prevent object with ID 2 from appearing in the options Here's what I've got:
http://jsfiddle.net/9d2Za/
<div ng-app ng-controller="ctrl">
Unfiltered:
<select ng-model="selectedBidType"
ng-options="bidType.id as bidType.label for (bidTypeID, bidType) in bidTypes">
</select>
<br>
Filtered:
<select ng-model="selectedBidType"
ng-options="bidType.id as bidType.label for (bidTypeID, bidType) in bidTypes | filter:{id: '!2'}">
</select>
</div>
Please note: I cannot change the structure of the bidTypes object in whatever fix we come up with. Here's my AngularJS:
function ctrl($scope)
{
$scope.selectedBidType = 1;
// Bid type objects are indexed by ID for fast lookup (this cannot be changed in solution)
$scope.bidTypes = {
1: {id: 1, label: "Buy"},
2: {id: 2, label: "Sell"},
3: {id: 3, label: "Missing"}
};
}
As described by the documentation, the filter filter only accepts an array as first parameter, not an object. In such cases, I personally use a custom filter to make the transformation:
myModule.filter(
'objectToArray',
[
function ()
{
return function (object)
{
var array = [];
angular.forEach(object, function (element)
{
array.push(element);
});
return array;
};
}
]
)
;
And then, in the template:
<select ng-options="… in bidTypes | objectToArray | filter:{id:'!2'}">

Resources