I am working on an angular application. I have to read data from json and process in two parts.
Read data from json using express: I have following Json in alphabetically sorted order.
{
"1": "Andy",
"2": "Billy"
"3": "Carry"
"4": "Doda"
}
Left hand side are Id's and Right one's are names. I have to read this data using express and access this data in my angular component.
In my angular component I have another array which is also sorted. That array is as follows:
[
{
"Name": "Andy",
"Id": "1"
"IncomingTime": "2020-06-19T11:02+00:00",
"Outgoingtime": "2020-06-19T11:07+00:00"
},
{
"Name": "Billy",
"Id": "2"
"IncomingTime": "2020-06-19T11:05+00:00",
"Outgoingtime": "2020-06-19T11:17+00:00"
},
{
"Name": "Ciena",
"Id": 5
"IncomingTime": "2020-06-19T11:05+00:00",
"Outgoingtime": "2020-06-19T11:07+00:00"
},
{
"Name": "Doda",
"Id": "4"
"IncomingTime": "2020-06-19T11:05+00:00",
"Outgoingtime": "2020-06-19T11:07+00:00"
}
]
Once above Json data is available in component(JSON in point 1), I have to make a new array(or any data structure which can be sorted). In that new array I have to put data in such a way that alphabetically sorted "Name" coming from above Json should match with with data in sorted array. If Name is matched then content(Name, id, IncomingTime , OutgoingTime) is copied to new array for that particular "Name" which will also be sorted. If suppose While comparing names we came across a name for which data is not there in sorted array, we need to keep only Name in new array doesn't matter data is there or not. Like in above data for "Carry" data is not there in sorted array still in final array "Carry" should be there. How can I do that?
You can do it using Lodash. Lodash provides functions to achieve what you want. documentation is here.
install Lodash from here.
import Lodash import * as _ from 'lodash';
I think this is what you want
const arr = { 1: 'Andy', 2: 'Billy', 3: 'Carry', 4: 'Doda' };
const data = [
{
"Name": "Andy",
"Id": "1",
"IncomingTime": "2020-06-19T11:02+00:00",
"Outgoingtime": "2020-06-19T11:07+00:00"
},
{
"Name": "Billy",
"Id": "2",
"IncomingTime": "2020-06-19T11:05+00:00",
"Outgoingtime": "2020-06-19T11:17+00:00"
},
{
"Name": "Ciena",
"Id": 5,
"IncomingTime": "2020-06-19T11:05+00:00",
"Outgoingtime": "2020-06-19T11:07+00:00"
},
{
"Name": "Doda",
"Id": "4",
"IncomingTime": "2020-06-19T11:05+00:00",
"Outgoingtime": "2020-06-19T11:07+00:00"
}
];
const newArray = _.map(arr, item => {
const value = _.find(data, ['Name', item]);
const obj = value ? value : {Name: item};
return obj;
});
console.log(newArray);
Related
I am trying to filter some observable nested array in angular with the filter function in combination pipe function of the RxJs library.
Question:
I only want to show the categories with surveys given by a specific date.
Simplified situation:
My angular component has 3 radiobuttons (values 1,2,3). If i click on one of them it goes to my 'FilterChanged($event.value)' function. In this function i would like to filter the data that is provided by an api. This api at first provides all the categories. After retrieving the data i would like to filter according to the radio-button.
This is the data i get back from the api:
[
{
"category": "A",
"surveys": [
{
"day": "1",
"answers": [
{
"name": "1",
"value": "a"
},
{
"name": "2",
"value": "b"
},
{
"name": "3",
"value": "c"
}
]
},
{
"day": "2",
"answers": [
{
"name": "1",
"value": "a"
},
{
"name": "2",
"value": "b"
},
{
"name": "3",
"value": "c"
}
]
},
{
"day": "3",
"answers": [
{
"name": "1",
"value": "a"
},
{
"name": "2",
"value": "b"
},
{
"name": "3",
"value": "c"
}
]
}
]
},
{
"category": "B",
"surveys": [
{
"day": "2",
"answers": [
{
"name": "1",
"value": "a"
},
{
"name": "2",
"value": "b"
},
{
"name": "3",
"value": "c"
}
]
},
{
"answers": [
{
"name": "1",
"value": "a"
},
{
"name": "2",
"value": "b"
},
{
"name": "3",
"value": "c"
}
]
},
{
"day": "2",
"answers": [
{
"name": "1",
"value": "a"
},
{
"name": "2",
"value": "b"
},
{
"name": "3",
"value": "c"
}
]
}
]
}
]
If radio button 1 is selected i would like to only show the category A and only show it has 1 survey because thats the only survey matching the filter.
Whyd doesn't this code works?? the update filter gets triggerd at the radiobox change event. For me this is much more readable than the reduce with spreader functions.
updateFilter(filterDays : number): void {
var filterDate = this.getFilterDate(filterDays);
this.surveyTypes$ = this.allSurveyTypes$.pipe(map((types) => this.filterSurveyTypes(types, filterDate)));
}
filterSurveyTypes(types : SurveyType[], filterDate : Date) : SurveyType[] {
return types.filter(type => type.surveys.filter(survey => moment(survey.filledInDate).isSameOrAfter(filterDate)).length);
}
and a lot more variations but it does not seem to work.
I think i should not need a map because i am not transforming any data so filter should be fine but is not working for me so far.
I appreciate any help. Thanks
not positive what you're looking for, but it seems like you want to filter the outer array based on what's in the inner array and also filter the inner array, this can be achieved in one pass with reduce:
function filterOuterByInner(array, value) {
return array.reduce((acc, v) => {
const tmp = { ...v }; // create shallow copy
tmp.surveys = tmp.surveys.filter(a => a.day === value); // filter surveys array by value
if (tmp.surveys.length)
acc.push(tmp); // add to acc array if any surveys after filter
return acc;
}, []);
}
then just use it in your map:
this.categories$ = combineLatest(this.allcategories$, this.value$).pipe(map(([categories, val]) => filterOuterByInner(categories, val)));
This would work for you too:
let teste = [];
allcategories.forEach( category => {
category.surveys.forEach( survey => {
if (survey.day == '1'){
teste.push(survey)
}
})
})
It depends how you are using the observable, here are 2 examples :
If you want to set the categories in a property, not as observable, you have to use the subscribe method like this:
this.subscription = this.categories$.subscribe({
next: categories => this.categories = categories
});
then use this.categories. In this case do no not forget to call this subscription.unsubscribe() when destroying the component.
If you are using it in your component template with the async pipe it should work fine.
Remark: an observable is not activated if there is no subscribe (the async pipe does the subscribe and unsubscribe)
The problem I am seeing here is: the filter function is not synchronous. This means, when you call the second filter, the first is answered with a Promise-like response. Since your code is not asynchronous, filter will respond with all the elements, or none.
To solve this problem, you would have to declare your function as an asynchronous function.
Here's some example on how to do so:
async function awesomeFilter(allcategories){
return await allcategories.filter(category =>
category.surveys.filter(survey => survey.day == '1').length
)
}
The survey.day would be your nested verification;
The .length would return to the first filter 0 if no correspondences are found or positive value if there are, telling the first filter where the correspondences are.
This way I was able to make it work. Hopefully it will help you.
I am fairly new to Vue and JS but I am making API calls and getting a JSON response then sending the response to an empty array. How do I get the ID of each object in the array?
The array that the response is being pushed to is structured like this
groups: [
{
"id": "0",
"name": "a",
"price": 5
},
{
"id": "1",
"name": "b",
"price": 5
},
{
"id": "2",
"name": "c",
"price": 5
}
]
I'd like to pull the Id of each object and push the values to an empty array
for(var group in this.groups) {
if (this.groups.hasOwnProperty(0)) {
this.group = this.groups[0];
this.groupsId.push(this.innerObj);
}
}
The error I'm getting is saying Cannot read property '0' of undefined at eval
Ideally I'd like an array that has all the Ids of each object.
this.groups.hasOwnProperty(0) should be group.hasOwnProperty('id')
Use Array.prototype.map() to iterate over an array of objects and collect every ID into a new array:
const res = {
groups: [{
"id": "0",
"name": "a",
"price": 5
},
{
"id": "1",
"name": "b",
"price": 5
},
{
"id": "2",
"name": "c",
"price": 5
}
]
};
const ids = res.groups.map(obj => { // you use this.groups
if(obj.hasOwnProperty('id')) return obj.id;
});
console.log(ids)
There is the Array.map() method:
this.groupsId = this.groups.map(i => i.id);
If you already have elements in this.groupsId you can append the ids using Array.concat():
this.groupsId = this.groupsId.concat(this.groups.map(i => i.id));
You can use Array.prototype.reduce to loop and check if there's id.
const groups = [
{"name": "a","price": 5},
{"id": "1","name": "b","price": 5},
{ "id": "2","name": "c","price": 5}
];
const list = groups.reduce((groupIds, group) => group.id ? [...groupIds, group.id] : groupIds, []);
console.log(list);
I am fetching data from different sources and have ended up with a several correctly formed json objects and one which has parent keys like below:
{
"0": {
"term_id": 3,
"name": "Burger"
},
"1": {
"term_id": 6,
"name": "Chicken"
},
"2": {
"term_id": 12,
"name": "Mexican"
},
}
How can I remove 0, 1, 2, 3 etc while also preserving other correctly structured objects? I am using lodash elsewhere in this project
This object with parent keys is being inserted into an array of multiple objects via a map
Promise.all(promises)
.then(results => {
let valueArr = [];
Object.keys(results).forEach(function(key) {
valueArr = [results[key]]
});
this.setState({ categorySelectOptions: valueArr });
})
This is a screenshot of the output with the problem:
All you need to do is push into the array instead of assigning. Use this -
Object.keys(results).forEach(function(key) {
valueArr.push(results[key])
});
Use Object.values
const data = {
"0": {
"term_id": 3,
"name": "Burger"
},
"1": {
"term_id": 6,
"name": "Chicken"
},
"2": {
"term_id": 12,
"name": "Mexican"
},
};
console.log(Object.values(data));
Considering below object array:
[
{
"guid": "j5Dc9Z",
"courses": [
{
"id": 1,
"name": "foo",
}
]
},
{
"guid": "a5gdfS",
"courses": [
{
"id": 2,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
},
{
"guid": "jHab6i",
"courses": [
{
"id": 4,
"name": "foobar",
}
]
},
{...}
]
I am trying to filter an object array, comparing IDs in the nested courses array with in the below array:
filter.courses = [1,3]
The following line works for the nth value in the array: (via https://stackoverflow.com/a/41347441/9766768)
let fil = filter(this.results, { courses: [{ id: this.filter.courses[n] }]});
However, I'm hoping to achieve this (pseudo code below):
let fil = filter(this.results, { courses: [{ id: this.filter.courses }]});
Expected output would be an array of objects containing any of the course IDs elements, in this case:
[
{
"guid": "j5Dc9Z",
"courses": [
{
"id": 1,
"name": "foo",
}
]
},
{
"guid": "a5gdfS",
"courses": [
{
"id": 2,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
}
]
What would be considered the best solution in this case? Avoiding loops would be a bonus.
If you're trying to filter the elements whose course IDs contain in the filter.courses, you may use Array#every and Array#includes to do that:
const data = [{"guid":"j5Dc9Z","courses":[{"id":3,"name":"foo"}]},{"guid":"a5gdfS","courses":[{"id":1,"name":"bar"},{"id":3,"name":"foo"}]},{"guid":"jHab6i","courses":[{"id":7,"name":"foobar"}]}];
const courses = [1, 6, 3];
const r = data.filter(d => d.courses.every(c => courses.includes(c.id)));
console.log(r);
try this,
results = [
{
"guid": "j5Dc9Z",
"courses": [
{
"id": 3,
"name": "foo",
}
]
},
{
"guid": "a5gdfS",
"courses": [
{
"id": 1,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
}
]
var filter = [1]
console.log(results.map(result=>{
result.courses = result.courses.filter(course=>(filter.includes(course.id)))
return result
}))
Explore my recursive solution there: Playground Link
With this solution can nested array of objects being filtered from top level to botton level, layer by layer.
From what I understand the resulting array should contain all objects, that contain at least one course with an id that is contained in the array we use to filter.
So if an object exists with 2 courses - and one of them has an id we are looking for this object should then be part of the array that gets returned (see object with property "guid" : "a5gdfS" in the questions example)
With one little tweak the code provided in the answer by 31piy (marked as best by question owner) will do exactly what we desire. To do so we just change the array method every() to the array method some().
const r = data.filter(d => d.courses.every(c => courses.includes(c.id)));
const r = data.filter(d => d.courses.some(c => courses.includes(c.id)));
With the method every() the resulting array will only contain the objects, where each and every course has an id we are looking for. ("guid": "a5gdfS" is not in the resulting array)
With the method some() the resulting array will contain the objects, where at least one course has an id we are looking for ("guid": "a5gdfS" is in the resulting array)
/* arrays are the same as provided in the question
so that we can check against expected/desired output in the question */
const data = [{
"guid": "j5Dc9Z",
"courses": [{
"id": 1,
"name": "foo",
}]
},
{
"guid": "a5gdfS",
"courses": [{
"id": 2,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
},
{
"guid": "jHab6i",
"courses": [{
"id": 4,
"name": "foobar",
}]
}
];
const courses = [1, 3];
//array contains all objects with at least 1 course that has an id we are looking for
const r = data.filter(d => d.courses.some(c => courses.includes(c.id)));
console.log("code with some " + JSON.stringify(r));
//array contains only objects, whose classes all have an id we are looking for
const o = data.filter(d => d.courses.every(c => courses.includes(c.id)));
console.log("code with every " + JSON.stringify(o));
depending on what we are trying to do either every() or some() might be correct - depends on what we are trying to achieve
Using Javascript I am trying to create one array from a JSON object. Problem is is that I am trying to get the acceptedAnswers which is an array. I figured map was the best function for this. However what is returned is an array of arrays. I want to get on array of the values only. Thanks for your help.
Code
let categories = [
{
"categoryID": "1",
"categoryName": "Fruits with seeds",
"acceptedAnswers": [
"2","5"
]
},
{
"categoryID": "2",
"categoryName": "Fruits without seeds",
"acceptedAnswers": [
"1","3","4"
]
},
{
"categoryID": "3",
"categoryName": "Blue Fruit",
"acceptedAnswers": [
"1"
]
}
]
JS
let catAcceptedAnswerArray = categories.map(function (cat) {
return cat.acceptedAnswers
})
console.log(catAcceptedAnswerArray)
Returns:
0: ["2","5"]
1: ["1","3","4"]
2: ["1"]
I am trying to return:
0: ["2","5","1","3","4","1"]
Here is a fiddle
map isn't the right tool to reach for here. Simply create an array and append each object's entries to it:
const result = [];
for (const category of categories) {
result.push(...category.acceptedAnswers);
}
Or if you can't use ES2015+ features, an ES5 version:
var result = [];
categories.forEach(function(category) {
result.push.apply(result, category.acceptedAnswers);
});
This assumes there won't be thousands and thousands of acceptedAnswers; a single call to push has platform-specific limits (but again, they're in the thousands).
Like almost all array operations, it's possible to shoehorn this into reduce, but you don't gain anything by it:
var result = categories.reduce(function(arr, category) {
arr.push.apply(arr, category.acceptedAnswers);
return arr;
}, []);
With ES6 you can try with:
categories
.map(category => category.acceptedAnswers)
.reduce((acc, ids) => acc.concat(ids), [])
You can simply reduce the Array to first level after mapping using Array#concat
let categories = [{
"categoryID": "1",
"categoryName": "Fruits with seeds",
"acceptedAnswers": [
"2", "5"
]
},
{
"categoryID": "2",
"categoryName": "Fruits without seeds",
"acceptedAnswers": [
"1", "3", "4"
]
},
{
"categoryID": "3",
"categoryName": "Blue Fruit",
"acceptedAnswers": [
"1"
]
}
]
let catAcceptedAnswerArray = categories.map(cat => cat.acceptedAnswers).reduce((acc, val) => acc.concat(val), []);
console.log(catAcceptedAnswerArray)