Reactjs looping through array and switch - json

I have a reactjs component and I am trying to render the data with a loop and switch.
I tried a map and then a forEach -- but it claims its not a function.
the json looks like this.
//json
"contents": {
"0": ["emotional distress", "behavioural difficulties", "hyperactivity and concentration difficulties", "difficulties in getting along with other young people"],
"5": ["kind and helpful behaviour"]
}
//component
var YourCurrentStanding = React.createClass({
alertLevel: function (key) {
switch (key) {
case '0': return "very high";
case '1': return "high";
case '5': return "very low";
default: return "xy";
}
},
render: function () {
console.log('this.props.data.contents', this.props.data.contents)
return (
<div>
{
this.props.data.contents.forEach(function(j) {
return <p key={j}>Score for x,y and z {this.alertLevel(j)}</p>
})
}
</div>
);
}
});
----------- should read like
"<p>Score for emotional distress, behavioural difficulties, hyperactivity and concentration difficulties very high and difficulties in getting along with other young people very high</p>
<p>Score for kind and helpful behaviour very low</p>"
near working code with a grammar check added
var YourCurrentStanding = React.createClass({
grammarCheck : function(vals) {
//x,y,z and d
//z
return vals.join(', ')
},
alertLevel: function(key) {
switch (key) {
case "0":
return "very high";
case "1":
return "high";
case "5":
return "very low";
default:
return "xy";
}
},
render: function() {
return (
<div>
{Object.keys(this.props.data.contents).map((key, index) => {
return <p key={index}>Score for {this.grammarCheck(this.props.data.contents[key])} is <b>{this.alertLevel(key)}</b></p>
})}
</div>
);
}
});

Here it is: (Took sample data object for demo)
var data = {
contents: {
"0": [
"emotional distress",
"behavioural difficulties",
"hyperactivity and concentration difficulties",
"difficulties in getting along with other young people"
],
"5": ["kind and helpful behaviour"]
}
};
var YourCurrentStanding = React.createClass({
alertLevel: function(key) {
switch (key) {
case "0":
return "very high";
case "1":
return "high";
case "5":
return "very low";
default:
return "xy";
}
},
joinContents: function(data, key) {
return [ data[key].slice(0, -1).join(", "), data[key].slice(-1)[0]
].join(data[key].length < 2 ? "" : " and ");
/*
data[key].slice(0, -1).join(", "): takes all keys except last and joins them together with a comma.
data[key].slice(-1)[0]: Last key
*/
},
render: function() {
return (
<div>
{Object.keys(data.contents).map((key, index) => {
return (
<p key={index}>
Score for {this.joinContents(data.contents, key)} is {" "}
<b>{this.alertLevel(key)}</b>
</p>
);
})}
</div>
);
}
});
ReactDOM.render(<YourCurrentStanding />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"/>

Just use arrow function this way. And as #cesar william said, take care of maping on object properties.
render: function () {
console.log('this.props.data.contents', this.props.data.contents)
return (
<div>
{
Object.keys(this.props.data.contents).map((key, index) =>
{
return <p key={index}>Score for xx {this.alertLevel(this.props.data.contents[j])}</p>
})
}
</div>
);
}
I didn't really look at your jsx paragraph but it's a good start.
Edit: you should never use index as key, find something that fits better, it was just for the example

You can't iterate object by forEach or map methods.
const contentKeys = Object.keys(this.props.data.contents);
contentKeys.map(function(key, index) {
const item = this.props.data.contents[index];
return <p key={index}>Score for xx {this.alertLevel(item)}{index !== contentKeys.length-1 ? ', ' : null}</p>
});
Call alertLevel method with index or item – it's up to you.

Related

Console.log json specific value

I'm working with some script and I would like to ask how to display on the console a specific json value.
For example, I have script:
Promise.all([
fetch('https://blockchain.info/balance?active=3C6WPNa5zNQjYi2RfRmt9WUVux7V4xbDmo').then(resp => resp.json()),
fetch('https://api.binance.com/api/v3/avgPrice?symbol=BTCEUR').then(resp => resp.json()),
]).then(console.log)
output:
[{
3C6WPNa5zNQjYi2RfRmt9WUVux7V4xbDmo: {
final_balance: 185653,
n_tx: 1,
total_received: 185653
}
}, {
mins: 5,
price: "19230.49330261"
}]
I want to console price and final_balance.
Best regards!
One way you could achieve this is by flattening the array and objects within because there's no predefined structure of what the output looks like.
Here, I'm assuming the output you mentioned is always an array of objects.
const flattenObject = (obj = {}) =>
Object.keys(obj || {}).reduce((acc, cur) => {
if (typeof obj[cur] === "object") {
acc = { ...acc, ...flattenObject(obj[cur]) };
} else {
acc[cur] = obj[cur];
}
return acc;
}, {});
const outputs = [
{
"3C6WPNa5zNQjYi2RfRmt9WUVux7V4xbDmo": {
final_balance: 185653,
n_tx: 1,
total_received: 185653,
},
},
{
mins: 5,
price: "19230.49330261",
},
];
outputs.forEach((output) => {
const flatOutput = flattenObject(output);
console.log("flatOutput:", flatOutput);
if (flatOutput.final_balance) {
console.log("final_balance:", flatOutput.final_balance);
}
if (flatOutput.price) {
console.log("price:", flatOutput.price);
}
});

RowDataPacket returns empty object but it is not empty [React/Next]

I've been stressing around trying to fix this and I've burnt myself out. I'm calling my serverless mysql trying to get kanbans from teams. I've used this method multiple times and all were working fine but that is most likely because of they only return single item whilst this returns multiple items.
This is my code which returns empty object.
async function getKanbans(team_id){
let kanbans = [];
await sql_query(`SELECT id, sName FROM table WHERE iTeam = ?`, [team_id])
.then(result => {
result.forEach(kanban => {
// console.log(kanban);
kanbans.push({
id: kanban.id,
name: kanban.sName
});
});
})
.catch(err => {
console.log(err);
});
console.log(kanbans);
return kanbans;
}
As you can see.. I am trying to print kanbans and I do get:
[
{ id: 1, name: 'Kanban_1' },
{ id: 2, name: 'Kanban_2' }
]
of out it. Then I'm trying to return it to the item that called this function and this is how that looks like:
teams.push({
id : team.id,
sName : team.sName,
sColor : team.sColor,
aKanbans : result[0]['selectedTeam'] == team.id ? getKanbans(team.id) : null,
});
(a small snippet of something bigger)
Okay, so now when I try and look at the data response (from the frontend) I get this:
{
"success": true,
"message": "Found teams",
"teams": [
{
"id": 1,
"sName": "Team1",
"sColor": "#fcba03",
"aKanbans": {}
},
{
"id": 2,
"sName": "Team2",
"sColor": "#2200ff",
"aKanbans": null
}
]
}
aKanbans from Team1 is empty, empty object. What the **** do I do? I tried mapping it and still got an empty object. React/javascript is not my main language, I just like to learn. Any suggestions?
You are mixing async / await function with normal Promises handling.
Try to change your getKanbans code like this:
async function getKanbans(team_id) {
let kanbans = [];
try {
const result = await sql_query(
`SELECT id, sName FROM table WHERE iTeam = ?`,
[team_id]
);
result.forEach((kanban) => {
kanbans.push({
id: kanban.id,
name: kanban.sName,
});
});
} catch (err) {
console.log(err);
}
return kanbans;
}
And then populate the teams using (declare the parent async):
teams.push({
id : team.id,
sName : team.sName,
sColor : team.sColor,
aKanbans : result[0]['selectedTeam'] == team.id ? getKanbans(team.id) : null,
});

Update JSON variable based on Mongoose results

I have a JSON variable outside of mongoDB collection as below
var outputJson = [
{
'Product' : 'TV',
'isSelected': 0
},
{
'Product' : 'Radio',
'isSelected': 0
},
{
'Product' : 'Book',
'isSelected': 0
},
{
'Product' : 'Watch',
'isSelected': 0
}
]
Now I want to update the isSelected key if the product exits in MongoDB; I want something like below
var outputJson = [
{
'Product' : 'TV',
'isSelected': 0
},
{
'Product' : 'Radio',
'isSelected': 1
},
{
'Product' : 'Book',
'isSelected': 0
},
{
'Product' : 'Watch',
'isSelected': 1
}
]
Here is the code that I am trying, but I am not getting the above result
outputJson.forEach(function(key,value){
wishlistData.find({userID:req.user.id}, function(err,data{
data.forEach(function(k,i){
if (data[i].product=== outputJson[value].Product){
outputJson[value].isSelected = 1
}
})
});
})
Any help is appreciated
Firstly, the callback function to forEach is called with the item in the array as the first argument, so data[i] and outputJson[value] are redundant.
You should make use of mongoose's findOne method to see if there's at least one match, and you can pass in Product as follows
outputJson.forEach(function(item) {
wishlistData.findOne({userID: req.user.id, product: item.Product}, function(err, data) {
if (data !== null) { // if it actually found a match
item.isSelected = 1;
}
});
});
But keep in mind that mongoDB queries are asynchronous, so outputJson would still be the same right after the forEach loop. You might want to use promises and Promise.all to ensure that you do stuff with outputJson after all the queries have been processed:
Promise.all(outputJson.map(function(item) {
return wishlistData.findOne({userID: req.user.id, product: item.Product}).then(function(err, data) {
if (data !== null) { // if it actually found a match
item.isSelected = 1;
}
});
})).then(function() {
// do stuff with outputJson here
});
Replace this
if (data[i].product=== outputJson[value].Product){
outputJson[value].isSelected = 1
}
by
if (data[k].product=== outputJson[key].Product){
outputJson[key].isSelected = 1
}
each callback function have first argument as index
First thing, why are you doing the same mongo query repeatedly for each object in the Array, as it will give you the same result. Also use the lodash library 'lodash'. This is how you can achieve this:
var _ = require('lodash')
wishlistData.find({userID:req.user.id}, function (err, data) {
var dataMap = _.indexBy(data, "product");
outputJson.forEach(function(key,value){
if(!_.isEmpty(_.get(dataMap, key.Product))) {
key.isSelected = 1;
}
})
}
Thank you #Ambyjkl, you guidence worked Promise made the trick, I made minor changes to your script and it started working
Promise.all(outputJson.map(function(i,k) {
return wishlistData.findOne({userID: req.user.id, product: i.Product}).then(function(data, err) {
if (data !== null) { // if it actually found a match
i.isSelected = 1;
}
});
})).then(function() {
console.log(outputJson);
});

Dividing a sorted list

I have a list of movies and need to group them in both c# (or angular is also acceptable) and css very similary to the image provided here underneath. Any ideas on how to wire the html and c# and how to use the .groupBy() or something similar please ?
This is what I've got so far:
HTML (a list of all my movies in alphabetical order):
<div class="movs">
<movies-collection movies="::vm.sortedMovies" order-by="name"></movies-collection>
</div>
Typescript:
static id = "MoviesController";
static $inject = _.union(MainBaseController.$baseInject, [
"sortedMovies"
]);
static init = _.merge({
sortedMovies: ["allMovies", (movies: Array<Models.IGov>) => {
return _.sortBy(movies, "content.name");
}]
All my movies are already sorted alphabteically I just need to with the help of css structure them similarly to this image
I would create a filter that adds a "$first" property to the movie. If it is the first in a sorted list that starts with the character, then $first would be true. Bind to $first in your view when you show the character in uppercase.
The following demonstrates this idea:
var app = angular.module('app',[]);
app.controller('ctrl', function($scope) {
$scope.movies = [
{ title: 'The Godfather' },
{ title: 'Fargo' },
{ title: 'Sniper' },
{ title: 'Terminator'},
{ title: 'Click'},
{ title: 'Cake' },
{ title: 'Frozen' },
{ title: 'Casino Jack' },
{ title: 'Superman' },
{ title: 'The Matrix' }
];
});
app.filter('applyFirst', function() {
return function (movies) {
for(var i = 0; i < movies.length; ++i) {
if (i == 0)
movies[i].$first = true;
else {
if (movies[i].title.toLowerCase()[0] != movies[i-1].title.toLowerCase()[0]) {
movies[i].$first = true;
}
else {
movies[i].$first = false;
}
}
}
return movies;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0-beta.1/angular.js"></script>
<div ng-app = "app" ng-controller="ctrl">
<div ng-repeat="movie in movies | orderBy:'title' | applyFirst">
<h1 ng-if="movie.$first">{{ movie.title[0] | uppercase }}</h1>
{{ movie.title }}
</div>
</div>
It's not possible in css, your code must split the array of movies into an array of letters, each with an array of movies.
You can use reduce for that:
var groupedMovies = movies.reduce((lettersArray, movie, idx, arr) => {
var firstLetter = movie[0].toUpperCase();
if (!lettersArray[firstLetter]) {
lettersArray[firstLetter] = [movie];
}
else {
lettersArray[firstLetter].push(movie);
}
return lettersArray;
}, []);
The result will look something like this:
[ T: [ 'The Avengers', 'Tower Quest', 'ThunderFist', 'Transformers' ],
U: [ 'Untamed Bengal Tiger', 'Untamed Giant Panda' ],
V: [ 'Victorious' ] ]
This way you can do a loop on the letters array, and in each do another loop for each movie.
The best practice for that would be to create a directive for a grouped movies, it will receive the letter and the inner array of movies in that letter.

AngularJS Filter Select array

I have a select that looks like this
<select
class="form-control"
ng-model="vm.transaction.location_from"
ng-options="l.name for l in vm.locations">
</select>
with vm.locations sourcing from the following JSON:
[
{
"id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e",
"name": "Location 1"
},
{
"id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2",
"name": "Location 2"
}
]
Further, I have another select that looks like:
<select
class="form-control"
ng-model="vm.transaction.item"
ng-options="i.name for i in vm.items">
</select>
with vm.items sourcing from the following JSON:
[
{
"id": "9f582e58-45dd-4341-97a6-82fe637d769e",
"name": "20oz Soft Drink Cup",
"locations": [
{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e"
},
{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2"
}
],
}
]
I want to, on change of the ng-mode="vm.transaction.item" select, have the ng-model="vm.transaction.location_from" be filtered to only show values that match from the locations array. I know I can use a | filter: { }, but I'm not sure what that filter should look like.
Hope this is your expected results.
Below are two options I tried ... demo | http://embed.plnkr.co/689OQztgu8F800YjBB2L/
Ref : underscorejs | angular-filter | everything-about-custom-filters-in-angular-js
// 1. filter items collection by location
angular.module('demo').filter('withLocation', function () {
return function (items, selectedLocation) {
function isLocationInLocations (elem) { return selectedLocation && elem.location_id === selectedLocation.id; }
function itemHasLocation (elm){ return (elm.locations && elm.locations.filter(isLocationInLocations).length > 0); }
return items.filter(itemHasLocation);
}});
// 2. filter function to check if option can be rendered ....
vm._filters.selectableItems = function(selectedLocation) {
return function(item) {
var locationsHasLocation = function(elem) { return selectedLocation && elem.location_id === selectedLocation.id; }
return (item.locations && item.locations.filter(locationsHasLocation).length > 0);
}
}
var app = angular.module("Test", []);
app.controller("Ctrl1", function($scope) {
$scope.location_fromArr =
[{
"id": "9f582e58-45dd-4341-97a6-82fe637d769e",
"name": "20oz Soft Drink Cup",
"locations": [{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e"
},{
"inventory_id": "9d5aa667-4a64-4317-a890-9b9291799b11",
"location_id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2"
}],
}];
$scope.itemArr =
[{
"id": "c0d916d7-caea-42f9-a87f-a3a1f318f35e",
"name": "Location 1"
},{
"id": "d8a299a3-7f4b-4d32-884f-efe25af3b4d2",
"name": "Location 2"
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="Test" ng-controller="Ctrl1">
Item
<select
class="form-control"
ng-model="item"
ng-options="i.name for i in itemArr">
</select>
Location
<select
class="form-control"
ng-model="location_from"
ng-options="l.name for l in location_fromArr | filter:{l.id: location_from.location_id}">
</select>
</div>
One way to do this is to supply a filter function to filter the locations. Something like:
vm.filterFun = function(selectedLocations) {
return function (location) {
var n;
if (!selectedLocations) {
return true;
}
for(n=0;n<selectedLocations.length;n += 1) {
if (selectedLocations[n].location_id === location.id) {
return true;
}
}
return false;
}
}
This is actually a function returning a filter function, based on the item selected.
Then in your select you apply the filter with:
<select
class="form-control"
ng-model="vm.transaction.location_from"
ng-options="l as l.name for l in vm.locations | filter:vm.filterFun(vm.transaction.item.locations)">
</select>
See plunker here.
I would forego angular filters and use the getterSetter option of ngModelOptions.
It could look something like this:
var selectedItem, selectedLocation;
var items = [];
var locations = [];
vm._items = items; // Static, always allow all items to be selected.
vm.locations = function () {
// Return differing results based on selectedItem.locations.
};
vm._transaction = {
location: function (v) {
/**
* If v is null, it is not present in the selectedItem.locations array.
* The extra check will ensure that we don't persist a filtered out location when
* selecting another item.
*/
return (v || v === null) ? (selectedLocation = v) : selectedLocation;
},
item: function (v) {
return v ? (selectedItem = v) : selectedItem;
}
};
Here's a plunker demonstrating the behaviour.
Not as simple/straight-forward as a filter, but I would bet (at least in the case of a piped filter) that you'd possibly see a slight performance gain going with this approach.
I do not have numbers to back up the above statement, and it usually boils down to the size of your dataset anyway. Grain of salt.
If you need it to function the other way around, you could write up a secondary filter like such:
function superFilter2 (arr) {
// If no location is selected, we can safely return the entire set.
if (!selectedLocation) {
return arr;
}
// Grab the current location ID.
var id = selectedLocation.id;
// Return the items that are present in the selected location.
return arr.filter(function (item) {
return item.locations.map(function (l) {
return l.location_id;
}).indexOf(id);
});
}
With that and the filter in the supplied plunker, there are some similarities that could be moved into higher order functions. Eventually with some functional sauce you could probably end up with a single god function that would work both ways.
you can do this:
<select
class="form-control"
ng-model="vm.transaction.item"
ng-change="itemCahngedFn()"
ng-options="i.name for i in vm.items">
</select>
var itemChangedFn = function(){
var filtredItems = [];
angular.forEach(vm.locations, function(item){
if(item.name == vm.transaction.item){
filtredItems .push(item.location);
}
});
vm.locations= filtredItems ;
}
i think filter:{ id : item.locations[0].location_id } should do the trick.
here is the jsfiddle
how do you think?