I am fetching some data via an API call (within my dataservicse.ts file) - and the response I get is a sort of complex JSON structure. I am able to fetch a specific part of the response as follows in my corresponding Component file as follows -
Here is one of the JSON object response that represents the general structure -
{
"address": {
"building": "1234",
"coord": [0, 0],
"street": "123 Main Street",
"zipcode": "00000"
},
"address2": "Test Suite 000",
"grades": [{
"grade": "A",
"score": 3
}, {
"grade": "B",
"score": 4
}, {
"grade": "A",
"score": 2
}],
"name": "John Doe",
"_id": "1212121"
}
Now - my goal is to acquire the 'name' attribute as well as the first 'grade' value within the grades attribute from each response object - and map them into separate Arrays so that I can display them in table columns using *ngFor.
This is my Component code
export class TestDataComponent implements OnInit {
name$: Object;
grade$: Object;
constructor(private data: DataService, private data2: DataService) { }
ngOnInit() {
//getAPIData is returning the API response from the dataservices.ts file
this.data.getAPIData().subscribe(
data=>console.log(data.response.map(name=>name.name))
); //this works fine - I get the names in an array
this.data2.getAPIData().subscribe(
data2=>console.log((data2.response.map(grade=>grade.grades)).map(grades
=> {grades.map((value, index) => value.grade})) //this returns an undefined value
);
}
Now - if I console.log((data2.response.map(grade=>grade.grades))
I get an Array of Array objects such as -
Array - [Array(3), Array(3), Array(2)]
and each of them consist of the 'grades' attribute Array of objects.
(taking the first array from above) -
Array(3)
0:{"grade": "A","score": 3}
1:{"grade": "B", "score": 4}
2:{"grade": "A", "score": 2}
Thus - I further map my initial response to achieve the 'grade' value. Also - I only want the first grade - thus I have a simple condition added as follows -
console.log((data2.response.map(grade=>grade.grades))
.map(grades
=> {grades.map((value, index) =>{if(index<1) value.grade})}))
As mentioned in the comment - I get an undefined value.
I undestand this issue may be fairly complex but I've tried my best to explain it as clearly as possible. My goal is to get the first 'grade' values from each object and display them in an Array - just as the names, so that I can use it to display it on a table.
I am fairly new to Angular6, just switching from Angular 1.6 - so I am pretty sure I am messing something up.
What would be the best way to get the grade values into an Array by nested mapping within subscribe? Or is there a better approach to the same ?
Also - for the sake of simplicity, ignore the fact that the first subscription is present (for the name attribute) - I showed it here so as to make it clear as to what I want to achieve.
Here is what I think you're asking for since you never gave a concrete example of what you're trying to map/reduce. Also, this is vanilla JavaScript but could easily be translated into RxJS.
// I am assuming that a call to `DataServce.getAPIData()` returns an array
const api_response = [{
"address": {
"building": "1234",
"coord": [0, 0],
"street": "123 Main Street",
"zipcode": "00000"
},
"address2": "Test Suite 000",
"grades": [{
"grade": "A",
"score": 3
}, {
"grade": "B",
"score": 4
}, {
"grade": "A",
"score": 2
}],
"name": "John Doe",
"_id": "1212121"
}];
// Map and pluck the values you need
const result = api_response.map(v => [v.name, v.grades[0].score])
// Or as an array of objects
const obj_result = result.map(([name, score]) => ({ name, score }))
// Is this what you want?
console.log('Array', result);
console.log('Objects', obj_result);
Quick Update
Thanks for accepting the answer. Just wanted to give a quick of what this might look like using RxJS. I say might because the code snippet below is untested.
this.nameAndScores$ = data.getAPIData().pipe(
map(({ response }) => response),
map(({ name, grades }) => ( { name, score: grades[0].score } ))
)
Assuming that nameAndScores$ is a property of your component instance, you can do *ngFor item in nameAndScores$ | async and avoid any explicit subscriptions in your code.
You are using curly braces with a fat arrow in second map function. While using curly braces, you should return a value using the return keyword.
Related
Using JavaScript for Zapier, I am trying to calculate the average value of a property from an array of object.
Here is one of the objects...
{
"code": 0,
"data": [
{
"id": "28737",
"owner": "1",
"date": "1581945706",
"dla": "0",
"dlm": "1582551517",
"system_source": "3",
"source_location": null,
"ip_addy": null,
"ip_addy_display": null,
"import_id": "0",
"contact_cat": "*/*",
"bulk_mail": "1",
"bulk_sms": "0",
"bindex": "76",
"f1849": "9898983",
"f1850": "Foundation Course 2: Lecture 1 QUIZ",
"f1851": "0",
"f1853": "John Doe",
"f1854": "TRUE",
"f1855": "93", // <= calculate average for this property
"f1859": "292",
"f1862": "0",
"f1867": "Kajabi",
"f1868": "0",
"unique_id": "7WB77PT"
},
...
]
}
I need to pull out the value for the property named f1855 for each object, then calculate the average and return that value via POST.
I don't think this is hard to do with JavaScript but I am not used to the rules and limits of Zapier.
Any help appreciated!
**Seeing the edited version of your post, I am now not sure if your input data is the data array inside the example or it is an array of those objects. If the second, then I don't know if you want an average for each object's data prop. or something else. But the code below could be part of the solution in either case.
I don't know anything about Zapier, but the JavaScript part could look something like this:
const inputData = //your array of objects
const reducer = (sum, theObject) => sum + parseFloat(theObject.f1855)
const sumF1855 = inputData.reduce(reducer, 0)
const avgF1855 = sumF1855 / inputData.length
This code does not handle error conditions (like if on of the objects were missing the f1855 property or divide by zero if inputData were empty). Hopefully it gives you an idea to get started.
I am trying to figure it out, how should I properly create a JSON model for a React component state.
Since now I always used Entity Framework and virtual properties to connect related "tables", so now when I want to do similar thing in React and JSON I have don't really know how to proceed.
This is my simplified model:
{
"myModel": {
"Categories": [
{
"Id": 1,
"Name": "Cat1",
"Active": true
},
{
"Id": 2,
"Name": "Cat2",
"Active": false
}
],
"Components": [
{
"Id": 1,
"Name": "Component1",
"CategoryId": 1
},
{
"Id": 2,
"Name": "Component2",
"CategoryId": 1
},
{
"Id": 3,
"Name": "Component3",
"CategoryId": 2
}
]
}
}
How to effectively join these two "tables"?
For example if I want to filter Components, whose Category is Active?
In my second approach I changed model to contain the whole Category object in Component:
..."Components": [
{
"Id": 1,
"Name": "Component1",
"Category": {
"Id": 1,
"Name": "Cat1",
"Active": true
}
},...
This allowed me to use filter(a=>a.Category.Active==true) function very easily, but then the problem is when I make changes to a property of one of the Categories, the change does not reflect to Components.
What is the best approach in this situation? Is is better to update all Component[].Category on each Category change or loop through all Categories to find the correct one each time I need to filter or group Components on CategoryId?
I need to have Categories in separate array, because they are not always all in use by Components.
You can easily aggregate the data and filter for active Components by using your data structure:
const activeComponents = myModel.Components.filter(component => {
let isActive = false;
const componentCategory = myModel.Categories.filter(
category => category.Id === component.CategoryId
);
if (componentCategory.length && componentCategory[0].Active)
isActive = true;
return isActive;
});
You can also shorten the code in case there is always a Category for each CategoryId:
const activeComponents = myModel.Components.filter(
component =>
myModel.Categories.filter(
category => category.Id === component.CategoryId
)[0].Active
);
You should check out the redux docs for that. You should not duplicate data and keep it as flat as possible. So your second approach is not advisable, because it both duplicates and nests the data. The components should be inserted into an object where the keys are the ids. Additionally, you could keep all active components in an string array, which contains all active component ids and and retrieve them by iterating over the active component array and extracting the component with the id from the mapping object.
This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 4 years ago.
I am getting an array of "product"s from a resolver getting data from a json endpoint.
ngOnInit() {
this.products = this._route.snapshot.data.products;
console.log('products: ', this.products);
}
where one of the objects in this array is in the format
{
"id": 3645,
"date": "2018-07-05T13:13:37",
"date_gmt": "2018-07-05T13:13:37",
"guid": {
"rendered": ""
},
"modified": "2018-07-05T13:13:37",
"modified_gmt": "2018-07-05T13:13:37",
"slug": "vpwin",
"status": "publish",
"type": "matrix",
"link": "",
"title": {
"rendered": "VPWIN"
},
"content": {
"rendered": "",
"protected": false
},
"featured_media": 0,
"parent": 0,
"template": "",
"better_featured_image": null,
"acf": {
"domain": "SMB",
"ds_rating": "3",
"dt_rating": ""
},
...
},
What I want to do is sort this array by the field title.rendered
In olden times, in AngularJS, I would simply use an orderBy pipe in the template set to this field. Apparently, this is removed in Angular and from doing research it seems the preferred method is to sort the data itself, such as in ngOnInit.
But I can't figure out how to sort products by producs.title.rendered.
You can simply use Arrays.sort()
array.sort((a,b) => a.title.rendered.localeCompare(b.title.rendered));
Working Example :
var array = [{"id":3645,"date":"2018-07-05T13:13:37","date_gmt":"2018-07-05T13:13:37","guid":{"rendered":""},"modified":"2018-07-05T13:13:37","modified_gmt":"2018-07-05T13:13:37","slug":"vpwin","status":"publish","type":"matrix","link":"","title":{"rendered":"VPWIN"},"content":{"rendered":"","protected":false},"featured_media":0,"parent":0,"template":"","better_featured_image":null,"acf":{"domain":"SMB","ds_rating":"3","dt_rating":""},},{"id":3645,"date":"2018-07-05T13:13:37","date_gmt":"2018-07-05T13:13:37","guid":{"rendered":""},"modified":"2018-07-05T13:13:37","modified_gmt":"2018-07-05T13:13:37","slug":"vpwin","status":"publish","type":"matrix","link":"","title":{"rendered":"adfPWIN"},"content":{"rendered":"","protected":false},"featured_media":0,"parent":0,"template":"","better_featured_image":null,"acf":{"domain":"SMB","ds_rating":"3","dt_rating":""}},{"id":3645,"date":"2018-07-05T13:13:37","date_gmt":"2018-07-05T13:13:37","guid":{"rendered":""},"modified":"2018-07-05T13:13:37","modified_gmt":"2018-07-05T13:13:37","slug":"vpwin","status":"publish","type":"matrix","link":"","title":{"rendered":"bbfPWIN"},"content":{"rendered":"","protected":false},"featured_media":0,"parent":0,"template":"","better_featured_image":null,"acf":{"domain":"SMB","ds_rating":"3","dt_rating":""}}];
array.sort((a,b) => a.title.rendered.localeCompare(b.title.rendered));
console.log(array);
Try this
products.sort(function (a, b) {
return a.title.rendered - b.title.rendered;
});
OR
You can import lodash/underscore library, it has many build functions available for manipulating, filtering, sorting the array and all.
Using underscore: (below one is just an example)
import * as _ from 'underscore';
let sortedArray = _.sortBy(array, 'title');
Not tested but should work
products.sort((a,b)=>a.title.rendered > b.title.rendered)
I have an array of objects as below. I am trying to achieve search functionality using .filter() method in my angular2 application.
[{
"label": "new",
"result": [{
"label": "new",
"fname": "abc",
"lname": "xyz"
}, {
"label": "new",
"fname": "abc1",
"lname": "xyz1"
}]},
{
"label": "old",
"result": [{
"label": "old",
"fname": "abc2",
"lname": "xyz2"
}]
}]
I am able to achieve parent/one level filtering using below code:
this.data.filter(item => (item.label.toLowerCase().indexOf(inputText) !== -1);
This is returning the object that matches value of label. I want to have filter on 'fname' and 'lname' also.
Your data structure is not well suited for doing an arbitrary string lookup from a 3 level data structure like this.
Your are going to end up having to iterate all three levels - the array of objects, the results array in each object, and all the properties of the objects in the results array.
This is an O(n^3) operation and would likely perform unsatisfactorily with a large data set. You may need to think about how you can better structure this data for this use case.
I would when I get the data from the server create a lookup variable inside every item which contains all text you want to be able to search.
But make sure this is only done once, since it's wouldn't be very performant if you did it for every keystroke.
// Add searchable string to users
users.forEach(u => {
u.filterTerm = `${(u.id || '').toLowerCase()} ${(u.email || '').toLowerCase()} ${(u.firstName || '').toLowerCase()} ${(u.lastName || '').toLowerCase()}`;
});
Then you can use the includes method to filter
let filtered = allUsers.filter(u => {
return u.filterTerm.includes(filterTerm);
});
How to refer to each property of an object in an array of objects in MongoDB MapReduce JavaScript query?
Here is my data:
{
"_id": ObjectId("544ae3de7a6025f0470041a7"),
"name": "Bundle 4",
"product_groups": [
{
"name": "camera group",
"products": [
{
"$ref": "products",
"$id": ObjectId("531a2fcd26718dbd3200002a"),
"$db": "thisDB"
},
{
"$ref": "products",
"$id": ObjectId("538baf7c26718d0a55000043"),
"$db": "thisDB"
},
{
"$ref": "products",
"$id": ObjectId("538baf7c26718d0a55000045"),
"$db": "thisDB"
}
]
},
{
"name": "lens group",
"products": [
{
"$ref": "products",
"$id": ObjectId("531e3ce926718d0d45000112"),
"$db": "thisDB"
},
{
"$ref": "products",
"$id": ObjectId("531e3ce926718d0d45000113"),
"$db": "thisDB"
}
]
}
]
}
Here is my map function: (for simplicity I took out the reduce option since it doesn't matter if the map doesn't work right)
var map = function() { emit(this.product_groups, this.product_groups.products); };
db.instant_rebates.mapReduce(
map,
{
out: "map_reduce_example",
query: {"_id": ObjectId("544ae3de7a6025f0470041a7")}
}
);
However the problem is that the "value" field in the result always comes up as "undefined". Why? Why doesn't this.product_groups.products return the products array? How do I fix this?
Also, I want it to do is to emit TWICE, once for each of the two product_groups. But so far it only emits ONCE. How do I fix that?
Under mapReduce operations the documents are presented as JavaScript objects so you need to treat them as such and traverse them. That means processing each member of the array:
var map = function() {
this.product_groups.forEach(function(group) {
emit( group.name, { products: group.products } );
});
};
var reduce = function(){};
db.instant_rebates.mapReduce(
map,
reduce,
{
out: "map_reduce_example",
query: {"_id": ObjectId("544ae3de7a6025f0470041a7")}
}
);
The requirements of the "emit" function is both a "key" and a "value" to be presented as arguments which are emitted. The "value" must be singular therefore to emit an "array" of data you need to wrap this under the property of an object. The "key" must be a singular value as it's intent to it be used as the "grouping key" in the reduce operation, and the "name" field should be sufficient at least for example.
Naturally since there is a top level array in the document you process "each element" as is done with the function, and then each result is "emitted" so there are "two" results emitted from this one document.
You also need to at least define a "reduce" function even if it never gets called because all of the emitted keys are different, as is the case here.
So, it's JavaScript. Treat a list structure as a list.
Please note. This is all your question is about. If you want to ask further questions on mapReduce then please ask other questions. Don't ask any more of this one. I don't want to talk about your field naming, or go into detail of how this seems to be working towards "how do I pull in data from the other collection", which is something you cannot do.