Angular - Display data from 2 JSON in HTML - html

I'm having trouble displaying data from 2 interconnected JSON in Angular.
First JSON:
member = [
{ id: 1, name: 'shinta'},
{ id: 2, name: 'rahma'},
{ id: 3, name: 'john'}
]
Second JSON:
nest = [
{id: 1, amount: 2000, userid: 1},
{id: 2, amount: 3000, userid: 2},
{id: 3, amount: 5000, userid: 2},
{id: 4, amount: 1500, userid: 1},
{id: 5, amount: 2200, userid: 1},
{id: 6, amount: 12500, userid: 1},
{id: 7, amount: 7000, userid: 2},
{id: 8, amount: 8300, userid: 3},
{id: 9, amount: 2800, userid: 3},
{id: 10, amount: 9500, userid: 3}
]
How to view/bind data in HTML?
<ul *ngFor="let item of memberData, let i = index">
<li>{{ item.name }} <span>*this to view sum of user amount*</span></li>
</ul>
Please guide me to solve this problem. Thank you.

Use .map() to create new array with:
Duplicate object element of members (via ...x)
New field: totalAmount with .reduce() to perform aggregate sum.
this.memberData = this.member.map((x) => ({
...x,
totalAmount: this.nest
.filter((y) => y.userid == x.id)
.reduce((total: number, current) => (total += current.amount), 0),
}));
<ul *ngFor="let item of memberData; let i = index">
<li>
{{ item.name }} <span>{{ item.totalAmount }}</span>
</li>
</ul>
Demo on StackBlitz

Related

How to left join two json array based on common id using RxJS

Need help on a problem with Rxjs join.
I have two observables with common id, I want to perform join to show records
from both objects based on id.
let emp = of([
{ id: 1, name: 'aa', age: 20 },
{ id: 2, name: 'bb', age: 21 },
{ id: 3, name: 'cc', age: 22 },
{ id: 4, name: 'dd', age: 23 }
]);
let details = of([
{ id: 1, address: 'ee' },
{ id: 1, address: 'ff' },
{ id: 2, address: 'gg' },
{ id: 2, address: 'hh' }
]);
I need the join like this:
[
{id: 1, name: 'aa', age: 20, details: [{address: 'ee'},{address: 'ff'}] },
{id: 2, name: 'bb', age: 21, details: [{address: 'gg'},{address: 'hh'}] },
{id: 3, name: 'cc', age: 22, details: undefined },
{id: 4, name: 'dd', age: 23, details: undefined },
]
I have tried like this:
forkJoin([emp, details]).pipe(
map(([_emp, _details]) =>
_emp.map((item) => {
return ( {
...item,
details: _details.find((item1) => {
return item1.id === item.id
})
})
})
)
).subscribe(console.log)
I got the result:
[
{id: 1, name: 'aa', age: 20, details: [{address: 'ee'}] }, // {address: 'ff'} missing
{id: 2, name: 'bb', age: 21, details: [{address: 'gg'}] }, // {address: 'hh'} missing
{id: 3, name: 'cc', age: 22, details: undefined },
{id: 4, name: 'dd', age: 23, details: undefined },
]
In this result second address is missing for id 1 and 2.
Instead of [{address: 'ee'},{address: 'ff'}], I got only first match [{address: 'ee'}] for id 1.
Instead of [{address: 'gg'},{address: 'hh'}], I got only first match [{address: 'gg'}] for id 2.
Please help me to accomplish the desired result
I think you should use filter() instead of find(). The find() operator returns only the first match.
forkJoin([emp, details]).pipe(
map(([_emp, _details]) =>
_emp.map((item) => {
return ({
...item,
// This is the line you have to modify:
details: _details.filter((item1) => item1.id === item.id)
})
})
)
).subscribe(console.log)

Join JSON with a common attribute

I have a JSON with product items with a code, name and quantity. Sometimes items are duplicated (same code and name, different quantity). I'd like to sum the quantity of them in a single one.
Ex:
FROM
{ items: [{ code: 1, name: 'A', quantity: 2.0 }, { code: 1, name: 'A', quantity: 3.0 }, { code: 2, name: 'B', quantity: 4.0 }] }
TO
{ items: [{ code: 1, name: 'A', quantity: 5.0 }, { code: 2, name: 'B', quantity: 4.0 }] }
This should work:
items_hash = { items: [{ code: 1, name: 'A', quantity: 2.0 }, { code: 1, name: 'A', quantity: 3.0 }, { code: 2, name: 'B', quantity: 4.0 }] }
items_hash[:items] = items_hash[:items].each_with_object([]) do |item, collected_items|
if item_in_collected_items = collected_items.find { |i| i[:code] == item[:code] }
item_in_collected_items[:quantity] += item[:quantity]
else
collected_items << item
end
end
p items_hash
#=> { items: [{ code: 1, name: 'A', quantity: 5.0 }, { code: 2, name: 'B', quantity: 4.0 }] }
Code
def group_em(h)
h.transform_values do |a|
a.each_with_object({}) do |g,h|
h.update([g[:code], g[:name]]=>g) do |_,o,n|
g.merge(quantity: o[:quantity] + n[:quantity])
end
end.values
end
end
Examples
h = { items: [{ code: 1, name: 'A', quantity: 2.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 2, name: 'B', quantity: 4.0 }] }
group_em(h)
#=> { items: [{ code: 1, name: 'A', quantity: 5.0 },
# { code: 2, name: 'B', quantity: 4.0 }] }
h = { items: [{ code: 1, name: 'A', quantity: 2.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 1, name: 'C', quantity: 6.0 },
{ code: 2, name: 'B', quantity: 4.0 }] }
group_em(h)
#=> {:items=>[{:code=>1, :name=>"A", :quantity=>5.0},
# {:code=>1, :name=>"C", :quantity=>6.0},
# {:code=>2, :name=>"B", :quantity=>4.0}]}
h = { items: [{ code: 1, name: 'A', quantity: 2.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 1, name: 'C', quantity: 6.0 },
{ code: 2, name: 'B', quantity: 4.0 }],
returns: [{ code: 2, name: 'B', quantity: 1.0 },
{ code: 1, name: 'A', quantity: 3.0 },
{ code: 1, name: 'C', quantity: 1.0 },
{ code: 2, name: 'B', quantity: 2.0 }]
}
group_em(h)
#=> {:items=>[{:code=>1, :name=>"A", :quantity=>5.0},
# {:code=>1, :name=>"C", :quantity=>6.0},
# {:code=>2, :name=>"B", :quantity=>4.0}],
# :returns=>[{:code=>2, :name=>"B", :quantity=>3.0},
# {:code=>1, :name=>"A", :quantity=>3.0},
# {:code=>1, :name=>"C", :quantity=>1.0}]}
Explanation
See Hash#transform_values, Enumerable#each_with_object, Hash#merge and the form of Hash#update (aka merge!) that employs a block to determine the values of keys that are present in both hashes being merged. Here that block is:
do |_,o,n|
g.merge(quantity: o[:quantity] + n[:quantity])
end
The values of the three block variables, _, o and n, are defined in the doc. I've used an underscore (a valid local variable) as the first variable--the common key--to signify that it is not used in the block calculation. That is common practice.
Note that:
h.update([g[:code], g[:name]]=>g)
is shorthand for:
h.update({ [g[:code], g[:name]]=>g })

Dropdown default selection in Angular

I have following dropdown implementation in Angular. But I want to display the Paris as a default , but it shows None even though I assign value as 1 as follows.
.html
<p-dropdown [options]="labels" [(ngModel)]="selectedCity" optionLabel="name" (onChange)="cityChanged($event.value)"></p-dropdown>
.ts
interface City{
name: string;
value: number;
}
export class CityComponent {
selectedCity: number = 1;
constructor() {
this.labels = [
{name: 'None', value: 0},
{name: 'Paris', value: 1},
{name: 'Rome', value: 2},
{name: 'London', value: 3},
{name: 'Istanbul', value: 4},
{name: 'Amsterdam', value: 5},
{name: 'Moscow', value: 6},
{name: 'Zurich', value: 7}
];
}
cityChanged(city : City)
{
this.selectedCity = city.value
}
}
This is happening because the dropdown expects the selected value to match one of your options. Since you are storing your options as objects, but your value as a number, it is unable to find a match. The simplest approach to fix this would be to store the whole object as the selectedCity, grabbing just the value once it is needed:
export class CityComponent {
selectedCity = {name: 'Paris', value: 1};
constructor() {
this.labels = [
{name: 'None', value: 0},
{name: 'Paris', value: 1},
{name: 'Rome', value: 2},
{name: 'London', value: 3},
{name: 'Istanbul', value: 4},
{name: 'Amsterdam', value: 5},
{name: 'Moscow', value: 6},
{name: 'Zurich', value: 7}
];
}
getSelectedValue(): number {
return this.selectedCity.value;
}
cityChanged(city : City) {
this.selectedCity = city;
}
}

Trouble formatting json objects with lodash _.groupBy

I'm having trouble reformatting an object in order to group by lists.
// input
{
'M': [
{name: Bob, id: 1},
{name: John, id: 2},
{name: John, id: 3},
],
'F': [
{name: Liz, id: 4},
{name: Mary, id: 5},
{name: Mary, id: 6},
]
}
// desired output
{
'M': [
'Bob': [ {name: Bob, id: 1},]
'John': [ {name: John, id: 2}, {name: John, id: 3} ]
],
'F': [
'Liz': [ {name: Liz, id: 4} ]
'Mary': [ {name: Mary, id: 5}, {name: Mary, id: 6} ]
]
}
My current script is only returning the 'M' key and I'm not what is causing it
for (var key in obj) {
var data = _.groupBy(obj[key], 'name')
return data;
}
I've also tried
Object.keys(obj).forEach(obj, key => {
var data = _.groupBy(obj[key], 'name')
return data;
})
but it throws TypeError: #<Object> is not a function
You can use mapValues to group each gender groups by their names through groupBy.
var output = _.mapValues(input, names => _.groupBy(names, 'name'));
var input = {
'M': [
{name: 'Bob', id: 1},
{name: 'John', id: 2},
{name: 'John', id: 3},
],
'F': [
{name: 'Liz', id: 4},
{name: 'Mary', id: 5},
{name: 'Mary', id: 6},
]
};
var output = _.mapValues(input, names => _.groupBy(names, 'name'));
console.log(output);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Use _.forOwn since you want to iterate its own properties.
_.forOwn(obj, function(key, val) {
var ret = {};
ret[key] = _.groupBy(val, 'name');
return ret;
});

ngModel Acting Strange - Angular2

myList: any;
ngOnInit(){
this.myList = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
];
}
In my html
<input *ngFor="let l of myList" [(ngModel)]="l.name" name="{{l.id}}" />
It's working good as below:
Chrome Dev:
Then when I call another service in my ngOnInit like below:
myList: any;
ngOnInit(){
this.myList = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
];
this.outletService.getAll().then(outlets => {
this.outlets = outlets;
});
}
Suddenly, there is no value in the input as you see below: My html code is the same.
What could possibly be happened? How can I get the value to appear in the inputs?
Here is the outletService .getAll()
getAll() {
return new Promise(resolve => {
let headers = new Headers({
'Content-Type': 'application/json'
});
let options = new RequestOptions({ headers: headers });
this.http.get(this.host + '/outlets', options)
.subscribe(data => {
resolve(data.json()); //Return array of objects
});
});
}
Here is the return from the outletService:
There is no error in the console, and I want the property name from this.myList, not outletName from this.outlets.
Update
What's more interesting is: I don't even need to call another service:
If i just assign new array to this.outlets, it won't work, like this case:
myList: any;
outlets: any;
ngOnInit(){
this.myList = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
];
this.outlets = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
];
}
And add another line to the html file.
<input *ngFor="let l of myList" [(ngModel)]="l.name" name="{{l.id}}" />
<input *ngFor="let o of outlets" [(ngModel)]="o.outletName" name="{{o.id}}" />
You should change your template. There is no name attribute on your returned data:
<input *ngFor="let l of myList" [(ngModel)]="l.outletName" name="{{l.id}}" />
Ohh, well.. Gunter already mentioned this in his comment :)
In fact, the ngModel doesn't give any values to the input because the id properties of the two objects are the same!
If I change from this:
myList: any;
outlets: any;
ngOnInit(){
this.myList = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
];
this.outlets = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
];
}
To this:
myList: any;
outlets: any;
ngOnInit(){
this.myList = [
{id: 1, name: 'a'},
{id: 2, name: 'b'},
{id: 3, name: 'c'},
{id: 4, name: 'd'},
];
this.outlets = [
{id: 5, name: 'a'},
{id: 6, name: 'b'},
{id: 7, name: 'c'},
{id: 8, name: 'd'},
];
}
Then it works! ask me why? I don't know.. If someone can explain would be great!