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.
Related
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);
}
});
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.
As I asked yesterday in my first post, I have a json file that looks like this:
groups:{[
{
title:Animal
shown:false
data:[{....}]
}
........
.....
]}
I want to change the shown value on a button click. The closest thing I found to my problem was this part of code:
newState = this.state.groups.map((val,i) => {
if(index === i){
return { ...val, shown: false};
}
return val;
})
this.setState({
groups: newState,
})
However, it doesn't seem to work, logging on console doesn't show any differences before and after the button press. I'm rather new to this so do you mind to help me understand what i did bad?
edit: I tried changing from index to a simple number to see if that was the problem, but still the same problem.
A JSON object is collection of Key Value pairs. i.e.
let FullName = {
firstName: "Stack",
lastName: "OverFlow"
}
In FullName Object Keys are firstName and lastName and corresponding values are "Stack" and "Overflow".
The groups Object that you have defined is missing the key Property.
Coming to Your problem:
Case1: If groups Object is an Array of Objects then:
var groups = [
{
title: 'Animal',
shown: false,
data: [{}]
},
{
title: 'Birds',
shown: false,
data: [{}]
}
]
/* Upadate By Index value */
/*
var index = 1;
let updatedGroup = groups.map((val,i) => {
if(index === i){
return { ...val, shown: true};
}
return val;
})
*/
/* Upadate By title */
/* let title = "Animal";
let updatedGroup = groups.map((val,i) => {
if(val.title === title){
return { ...val, shown: true};
}
return val;
}) */
// To toggle the shown Value Each Time
let title = "Animal";
let updatedGroup = groups.map((val,i) => {
if(val.title === title){
return { ...val, shown: !val.shown};
}
return val;
})
console.log("updatedGroup", updatedGroup);
Case2: If groups Object is Object of Objects then
var groups = {
group1: {
title: 'Animal',
shown: false,
data: [{}]
},
group2: {
title: 'Birds',
shown: false,
data: [{}]
}
}
let index = 1;
let updatedGroup = Object.values(groups).map((val, i)=>{
if(index === i){
return { ...val, shown: true};
}
return val;
})
console.log("updatedGroup",updatedGroup)
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.
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?