Add only unique items in a VUE.js v-for loop - javascript

I'm pulling a JSON formatted object from a Web API. The items are NIH funding grants and when pulled give a history of each Grant for a particular researcher. I'm only looking to render the max award_notice_date for each separate project_serial_num. I have the elements in descending order so if I do a v-for loop they are all rendered in descending order. So far so good. My question is how I can post only the first one of each of the project_serial_num's so I end up with the information from appl_id 10372234 and 10226173? Trying to filter by using dates does not help because a date may have expired but if it's the first one of that project_serial_num then it needs to be displayed
[{
"appl_id": 10372234,
"subproject_id": null,
"fiscal_year": 2022,
"project_num": "5R01CA261232-02",
"project_serial_num": "CA261232",
"award_notice_date": "2022-03-02T12:03:00Z"
},
{
"appl_id": 10226173,
"subproject_id": null,
"fiscal_year": 2021,
"project_num": "5K07CA214839-05",
"project_serial_num": "CA214839",
"award_notice_date": "2021-08-05T12:08:00Z"
},
{
"appl_id": 10253554,
"subproject_id": null,
"fiscal_year": 2021,
"project_num": "1R01CA261232-01",
"project_serial_num": "CA261232",
"award_notice_date": "2021-03-15T12:03:00Z"
},
{
"appl_id": 9989810,
"subproject_id": null,
"fiscal_year": 2020,
"project_num": "5K07CA214839-04",
"project_serial_num": "CA214839",
"award_notice_date": "2020-07-30T12:07:00Z"
}
]

I used this answer from another question to build this:
YOUR_ARRAY.filter((value, index, self) => self.findIndex(v => v.project_serial_num === value.project_serial_num) === index);
This will return a new array where the objects sharing a project_serial_num are removed, which can be passed directly to v-for, without the need to make a new variable/computed property.

You should create a computed property that will use a Map to store the values - but before adding a value to the Map you will first check whether it is already there and eventually skip the duplicates. Something like this
computed:
{
uniqueProjects()
{
const result = {};
this.apiResult.forEach(project =>
{
if (!result[project.project_serial_num])
{
result[project.project_serial_num] = project;
}
});
return Object.values(result); // we rely on the browser to return values in the order they have been added
}
}

Related

v-show if nested array items match filtered array

I have to write a Vue webapp that will take multiple filters, push them to an array and on a click method, check the filters arrays values and if any of the values match any of the nested values inside the tiles nested array, show the tiles where there is a match. So, my filter array could have:
filters: ['cookies', 'jogging']
And my nested tiles array will have:
tiles: [
{
"name": "Greg",
"food": ["cookies", "chips", "burgers"],
"activities": ["drawing", "watching movies"]
"favourite places": ["the parks", "movie theatre"]
},{
"name": "Robyn",
"food": ["cookies", "hotdogs", "fish"],
"activities": ["reading", "jogging"]
"favourite places": ["beach", "theme parks"]
},{
"name": "John",
"food": ["sushi", "candy", "fruit"],
"activities": ["writing", "planning"]
"favourite places": ["the moon", "venus"]
}
]
In the above example, the tiles that would show would be Robyn, since she likes cookies and jogging.
So far my thinking is writing out a for loop that checks the the values inside the nested array, which I got from this solution:
https://stackoverflow.com/a/25926600/1159683
However i'm failing to make the connection for just showing the item inside a v-for/v-show. I've got the method down for pushing all the filters to the filter array, but when it comes to matching it with the nested array and showing them based on the match, i'm at a loss. Preferably i'd like to write this out in vanilla js (es5).
Any help is appreciated.
Thank you!
computed: {
fullyMatchedTiles () {
// Matches must contain all terms from filter array
return this.tiles.filter(obj=> {
// Filter the filters to get matched count
let matchedFilters = this.filters.filter(filterItem=> {
// Check each property by looping keys
for (key in obj) {
// Only evaluate if property is an array
if (Array.isArray(obj[key])) {
// Return true if filterItem found in obj
if (obj[key].some(r=> filterItem.indexOf(r) >= 0)) {
return true
}
}
}
})
return this.filters.length === matchedFilters.length
})
},
partiallyMatchedTiles () {
// Matches must contain at least one term from filter array
// Check each object in the array
return this.tiles.filter(obj=> {
// Check each property by looping keys
for (key in obj) {
// Only evaluate if property is an array
if (Array.isArray(obj[key])) {
// Return true to the filter function if matched, otherwise keep looping
if (obj[key].some(r=> this.filters.indexOf(r) >= 0)) {
return true
}
}
}
})
},
},
Sorry it's not es5. I love the new features too much to take the time to go back 5 years.
For a full example showing the filtered object returned in vue, check this codepen https://codepen.io/shanemgrey/pen/jOErWbe
I think you were describing doing the filtering in the v-for. It seems like too complex of logic to try to accomplish it with the filtering available in v-for.
I would instead do as shown by breaking down the array in a new computed property and then using the resulting filtered array however you like in the template.

Accessing JSON value with unique named subarrays

I want to sort a JSON array based on time value in a subarray with the key names of the subarrays being named uniquely.
I'm searching for the method to access key, value update_time of every element in Products so I can use that value in a sorting script.
I have tried sorting the array but can not determine how to access the key, values of the subarrays
Expected behavior should be that every unique_keyname_# element is available for sorting and is sorted for further processing in JavaScript. Ultimately with the newest unique_keyname_# as the first element in a list, based on the update_time key.
var obj = {
"company": {
"department_1": {
"Products": {
"unique_keyname_1": {
"product_owner": "co-worker-1",
"update_time": "unix_timestamp_1"
},
"unique_keyname_5": {
"product_owner": "co-worker-4",
"update_time": "unix_timestamp_45"
},
"unique_keyname_8": {
"product_owner": "co-worker-2",
"update_time": "unix_timestamp_5"
}
}
},
"department_2": {
"Products": {
"unique_keyname_3": {
"product_owner": "co-worker-1",
"update_time": "unix_timestamp_21"
},
"unique_keyname_6": {
"product_owner": "co-worker-2",
"update_time": "unix_timestamp_7"
},
"unique_keyname_4": {
"product_owner": "co-worker-3",
"update_time": "unix_timestamp_75"
}
}
}
}
}
I solved the issue by writing an intermediate script in python which makes the API response a valid array. From there it was fairly easy to sort the data.
Thanks for the replies confirming the data itself was deliverd to me in an inappropriate format!
regards
In your example, there are no arrays.
Anyway, in Javascript you can access a node using . like:
obj.company.department_1.Products.unique_keyname_1
Or using [] which gives you more freedom to use costume fields
obj["company"]["department_1"]["Products"]["unique_keyname_1"]
// can also be more dynamic as:
obj["company"]["department_"+ department_counter]["Products"]["unique_keyname_" + keyname_counter]
Is there a possibility that you will change the structure of your JSON? to make it more manangeable ?
if so, i would recommend the folowing structure:
var products = [
{
department: 'SomeDepartment',
productName: 'Something',
productOwner: 'Someone',
update_time: 'Sometime'
}
]
Then you can sort the array easy using Array.sort()
for the sort topic use this : Sort array of objects by string property value

From an array of objects, extract value of properties for each object and put in a different array

I have a group of filters that is an Reactive Forms Object. I’ve taken the property values of the object and pushed it into an array.
// original filters object {claim_number: null, status: "Approved", patient: null, service_date: null}
let filterArr = []
Object.keys(this.filtersForm.value).forEach(filter => {
filterArr.push(this.filtersForm.value[filter])
// filterArr [null, “Approved, null, null]
})
I have a table that is comprised of an array of objects like the following:
"claims":[
{
"billed_amount":141.78,
"claim_number": "6596594-0",
"location":"University Hospital",
"member_id":"A1234567890",
"status":{
"label":"Approved",
"value": "Approved"
}
},
{
"billed_amount":341.70,
"claim_number": "2196524-3",
"location":"Springfield Hospital",
"member_id":"B1234567890",
"status":{
"label":"Pending",
"value":"Pending"
}
},
{
"billed_amount":111.70,
"claim_number": "1233514-5",
"location":"Springfield Hospital",
"member_id":"C1234567890",
"status":{
"label":"Pending",
"value":"Pending"
}
},
{
// ...etc
}
]
I am trying to loop through each row and put the property values in an array, one for each row so I can filter them against filterArr. How can I do that?
My question is similar to this post (From an array of objects, extract value of a property as array
), with the key difference being that I'm trying to create an array per object.
Each object represents a row in a table that I am trying to dynamically filter. So I can't have values from different rows being put into one array.
According to your desired result, I think you can use ES6 functions.
const result = yourTable.map(element => Object.values(element));
Using map() function, you go through all elements, and extract from each object its values.
Unsure what you want to include in your output but the below will loop through an array and return an array to the filter function
const output = claimTable["claims"].map((claim) => {
return claim
}).filter((claim) => {
return claim.billed_amount > 100
})
The above will loop through the claims and 'convert' to an array. The filter will return that claim for all true conditions (in this case, if the billed amount is greater than 100).
This article goes over this and adds a bit more to it.

Removing duplicates elements with conditionality

I have an array of objects that look like this:
[
{ project: "test1", date: "8/14/2018" },
{ project: "test1", date: "8/15/2018" },
{ project: "test2", date: "8/14/2018" }
]
I want to remove duplicate objects based on the project name, BUT, keep the one with the most recent date.
So my final array would like this:
[
{ project: "test1", date: "8/15/2018" },
{ project: "test2", date: "8/14/2018" }
]
I've come up with a disgruntled recursive solution that I'm unhappy with.
Looking for suggestions on a painless way to do this.
The approach I generally take for problems like this is to maintain a "dictionary" object to track uniqueness while iterating through, then extract the final result from that result. reduce is the go-to tool for creating one value from multiple values:
const dict = objects.reduce((result, item) => {
const currentForProject = result[item.project];
const isMostRecent =
!currentForProject
|| new Date(item.date) > new Date(currentForProject.date);
if (isMostRecent) {
result[item.project] = item;
}
return result;
}, {});
const mostRecents = Object.values(dict);
I will propose the general algorithm to solve your task but the implementation is up to you.
1) sort the array based on date field;
2) create an empty Set for storing project names and empty array for the result;
3) iterate over the sorted array and check:
3.1) if the Set does not contain current project name, then:
3.2) add the project name to the Set and add current item to your result
array;

How to retrieve a sorted list of objects in ydn-db?

Following an example from the docs here http://dev.yathit.com/ydn-db/getting-started.html, the first example under "Sorting".
My code:
var schema = {
stores: [
{
name: "authors",
keyPath: "id",
indexes: [
{ keyPath: "born" }
]
}
]
};
var db = new ydn.db.Storage("library", schema);
db.put("authors", [{ id: "111", born: "zzz" }, { id: "555", born: "bbb" }, { id: "999", born: "aaa" }]).done(function() {
// query with default ordering
db.values("authors").done(function(r) {
console.log("A list of objects as expected", r);
});
// query by ordered by "born" field
db.values(new ydn.db.Cursors("authors", "born", null, false)).done(function(r) {
console.log("This is a list of ids, not objects", r);
});
});
Changing the query from default ordering to ordering by a particular column seems to change its behaviour from returning a list of objects to just returning a list of ids. Am I doing something wrong? How do I get a list of objects?
It should be
// query by ordered by "born" field
db.values(new ydn.db.IndexValueCursors("authors", "born", null, false)).done(function(r) {
console.log("list of objects sorted by born", r);
});
or
// query by ordered by "born" field
db.values("authors", "born", null, false).done(function(r) {
console.log("list of objects sorted by born", r);
});
or simply
db.values("authors", "born").done(function(r) {
console.log("list of objects sorted by born", r);
});
A good API should do these common query very easily without reading documentation. I will think better API. For now, you got to read how iterator work: http://dev.yathit.com/api-reference/ydn-db/iterator.html Reference value of ydn.db.Cursors is primary key. That is why
values return list primary keys. Whereas reference value of ydn.db.IndexValueCursors is
record value, so values return list of objects. In fact, these are how IndexedDB API work.
Another point is above two queries has different performance characteristics. Second method, direct query is faster than first method, using iterator. This is because, iterator will iterate, whereas second method will use batch query. Performance is much different on websql since it does not support iteration.

Categories

Resources