Comparing two different data objects in Vue while reducing - vue.js

I have existing data objects and a function which is looping through by employee and doing some light math (all of this is currently working as it needs to)
The thing that I need to add into this is allow my loop to do another check as it moves through. As I'm moving through my data_record object, I need to check the ID in my Data_record object against the ID in the boxes. If they match, I need to check stock_numbers against box_numbers. If stock_numbers is greater than box_numbers then I want to put "Not Available" as a string into my computed object so that I can show that in my template instead of the data.
I have a sandbox linked below where one file is doing this code shown below, but the file main.js is trying to match up the entries so that I can then compare data on the matches, however, it is currently only returning the entries of the first one that don't have a match
How can I properly match entries between objects and compare data of those matches while still performing this current reduce function?
<tr v-for="(labels, employee) in numbersByEmployee" :key="employee">
<td>#{{ employee }}</td>
<td v-for="date in dates" :key="date">#{{ labels[date] && labels[date].qty * labels[date].labels }}</td>
<td>#{{ labels.total }}</td>
</tr>
new Vue({
el: "#app",
data: {
data_record: [
{
employee: "Adam",
ID: "Ac1874/12-15",
Shelf_Date: "2021-07-14",
stock_numbers: 100,
labels: 25
}
],
boxes: [
{
ID: "Ac1874/12-15",
box_numbers: 50,
reception_date: "2021-07-20"
}
],
dates: ["2021-07-14", "2021-07-15"]
},
computed: {
numbersByEmployee() {
return this.data_record.reduce((r, o) => {
r[o.employee] ??= {};
r[o.employee][o.Shelf_Date] ??= {
stock_numbers: 0,
labels: 0,
total: 0
};
r[o.employee][o.Shelf_Date].stock_numbers += +o.stock_numbers;
r[o.employee][o.Shelf_Date].labels += +o.labels;
r[o.employee].total ??= 0;
r[o.employee].total += +o.stock_numbers * +o.labels;
return r;
}, {});
}
}
});
Code sandbox with this portion of code, as well as a work in progress for the matching (the main.js file is where I'm just trying to match up the two objects but it's currently showing the entries that don't match)
https://codesandbox.io/s/charming-glitter-83nzg?file=/src/main.js
EDIT (output for reduce function)
Currently, numbersByEmployees dumps
"Adam": { "2021-07-14": { "labels": 5, "stock_numbers": 40, "total": 0 }, "total": 200 }

You can use array.prototype.find to look in the box array for the id of the current item in record_data
I've forked your examaple to demonstrate this. I renamed some of the variables to match your code on stackoverflow.
https://codesandbox.io/s/agitated-diffie-ntmru
Let me know if I can edit this to better fit your need.

Related

How do I split up an array while still selecting all items?

Let's say I have an array of fruits and an empty basket array. I push the fruits to the basket and loop through the basket in the template. I can output the whole array of fruits inside the basket.
<template>
<div v-for="fruit in basket">
<li>{{ fruit }}</li>
</div>
<button #click="addFruit">Add to basket</button>
</template>
<script>
data() {
return {
fruits: ['Orange', 'Apple'],
basket: [],
};
},
methods: {
addFruit() {
this.basket.push(this.fruits);
},
}
</script>
But what if I want each individual fruit to be shown as a list item? As it is right now I output the entire array of fruits.
I know that I can easilly get the individual fruits by saying
<li>{{ fruit[0] }}</li>
But that would not be practical as it requires a lot of manual work.
When I am pushing the fruits, I am looking for a way to also split them up, so that when I fire the addFruit function, I add all the fruits, but I add them as individual items.
I know there are other ways to do this, but I specifially want to know how I do this while keeping the arrays and the push method.
EDIT: I tried to write the fruit example because I wanted to keep it as simple as possible, but I will include my own code then.
In my code, I fetch an array of data from my database. I store the data results in a const called recipients. That is then pushed into an array called defaultTags. So I push an array of data into an empty array, in this case a list of emails and other contact information, which is then outputted as a tag, but what I want is to actually output the data as individual items. So instead of having one big tag that stores the whole array. Eg: [email1, email2, email3], I wish to have a seperate tag for each email in the array.
load() {
switch (this.entityType) {
case 'TENANCY':
userService.getCurrentUser().then(userResult => {
tenancyService.getTenants(this.entityId).then(result => {
const defaultTags = [];
const recipients = result.data
.map(tenant => tenant.legalEntity)
.filter(legalEntity => legalEntity.email || (!legalEntity.email && this.asNotification ? legalEntity.name : null))
.map(legalEntity => ({
emailAddress: legalEntity.email || (!legalEntity.email && this.asNotification ? legalEntity.name.concat(' ', `(${this.$t('letterMail').toLowerCase()})`) : null),
legalEntityId: legalEntity.id
}));
if (recipients.length) {
defaultTags.push(this.setText({description: this.$t('tenants'), recipients}));
}
this.autocompleteItems.push(...defaultTags);
if (this.includeUser) {
defaultTags.push(this.setText({
description: this.$t('user'),
recipients: [{emailAddress: userResult.data.email}]
}));
}
if (this.prefill) {
this.tagsChanged(defaultTags);
}
tenancyService.getUnits(this.entityId).then(result =>
result.data.forEach(unitTenancy => this.addPropertyContactsToAutocompleteItems(unitTenancy.unit.propertyId)));
});
});
break;
case 'UNIT':
unitService.get(this.entityId).then(result =>
this.addPropertyContactsToAutocompleteItems(result.data.propertyId));
break;
case 'PROPERTY':
this.addPropertyContactsToAutocompleteItems(this.entityId);
break;
}
},
I am focusing specifically on this line:
if (recipients.length) {
defaultTags.push(this.setText({description: this.$t('tenants'), recipients}));
}
It outputs the entire fruit items because you are pushing the whole array into the basket array, not the actual items. Saying basket is an array of array, not an array of fruits.
// Instead of this
this.basket.push(this.fruits); // [['Orange', 'Apple']]
// Use array destructuring
this.basket.push(...this.fruits); // ['Orange, 'Apple']
// Or concat
this.basket.concat(this.fruits); // ['Orange, 'Apple']

In vue, how do I filter a data object to only show values from the last 24 hours?

I have an array with some data objects that were created on various dates. I would like to only display the objects that were created within the last 24 hours.
I have tried to use moment for this, by using subtract on the date values, but it has no effect. Maybe someone here could come up with a suggestion.
Here are my computed properties. I use these because I am outputting the data in a bootstrap table, so the "key" represents the different values inside the object.
My table:
<b-card class="mt-4 mb-4">
<b-table
:items="tasks"
:fields="fields"
sort-desc
/>
</b-card>
My array (I am actually importing from a database, but for this question I will just write it manually) Please note I am just showing a single object here. In reality I have hundreds of objects
data: {
tasks: [
{ message: 'Foo' },
{ creationDateTime: '03-02-2022' },
{ isRead: false }
]
}
In my computed properties I then pass them to the table
computed: {
fields() {
return [
key: 'message',
label: 'message'),
sortable: true,
},
{
key: 'creationDateTime',
label: 'Date created',
formatter: date => moment(date).subtract(24, 'hours').locale(this.$i18n.locale).format('L'),
sortable: true,
},
{
key: 'isRead',
label: 'Has been read'),
sortable: true,
}
]
},
},
As I said, using subtract does not work. It still shows all objects in my database
I tried doing the reduction on the whole array as well, but I just get the error:
"TypeError: this.list.filter is not a function"
newTasks(){
if(this.tasks){
return moment(this.tasks.filter(task => !task.done)).subtract(24, 'hours')
}
}
I'm out of ideas.
In Moment, you can check if a date is within the last 24 hours with:
moment().diff(yourDate, 'hours') < 24
(note that future dates will also pass this check, but you can easily adjust it).
You can put this into your computed property:
newTasks(){
if(!this.tasks){
return []
}
return this.tasks.filter(task => !task.done && moment().diff(task.creationDateTime, 'hours') < 24)
}
And that's it, now newTasks should contain all tasks from the last 24 hours that are not done.

VueJS2: How to pluck out one property of an array and use it to find matching value in the second array?

I have two arrays. I am trying to pluck out a property from one array and use it to find the value of another property in the other way. How to do this? Let me explain:
I have an array of objects that looks like so:
languageCodes:
{
"code1234char3": "mdr",
"name": "Mandar",
},
{
"code1234char3": "man",
"name": "Mandingo",
},
{
// etc...
},
I have another array of objects that looks like so:
divisionLanguages:
[
{
p_uID: 1,
nameLang3Char: 'mdr',
},
{
p_uID: 2,
nameLang3Char: 'man'
},
{
// etc..
}
]
I have a Vue template with an unordered list like so:
<ul v-for="x in divisionLanguages" :key="x.p_uID">
<li>Name: FOO
<li>Language: {{x.nameLang3Char}} - XXX</li> <--how to get 'name' value from 'languageCodes' and place here?
</ul>
Expected output should be:
Name: FOO
Language: mdr - Mandar
Name: BAR
Language: man - Mandingo
I tried to do something like in Vue SFC template (but did not work):
<li>Language: {{ languageName(x.nameLanguage3Char) }}</li>
...
methods: {
languageName(nameLanguage3Char) {
const name = this.divisionLanguages.filter(x => x.code6392char3 === nameLanguage3Char)
return name.name
}
I hope this makes sense of what I am trying to do.
Update: Thanks to #kellen in the comments, I change from filte() to find() like so:
languageName(nameLang3Char) {
const languageName = this.languageCodes.find(
x => x.code1234char3 == nameLang3Char
)
return languageName
},
and in I did:
<li>Language: {{ languageName(x.nameLang3Char).name }}</li>
and it works...but I get error in console:
Error in render: "TypeError: Cannot read property 'name' of undefined"
Have you tried combining these arrays before rendering them? If you were able to combine both objects before creating that list, that would make your life easier. Another thing I noticed is you're using filter, when find might be a better option to return a single value rather than an array. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find

How to generate jQuery DataTables rowId client side?

The jQuery DataTables reference shows an example of setting the rowId option to a column from the data source (server side). This setting is used for the "select" extension and retaining row selection on Ajax reload.
Is there any way to generate the row identifier value 1) client side, or 2) as a combination of multiple columns from the data source?
Example data source:
{
"data": [
{
"aid": 5421,
"bid": 4502,
"name": "John Smith"
}
}
Code:
$("#datatable").DataTable({
select: true,
//rowId: "aid" each row ID is the value of the "aid" column
// e.g., <tr id="5421">
//rowId: 0 each row ID is the value of the 0-indexed column
// e.g., <tr id="5421"> (same as above)
rowId: [0, 1] // How? row ID combined value of 2+ columns
// e.g. <tr id="5421-4502">
rowId: "random" // How? random generated client-side ID
// e.g., <tr id="id34e04">
});
Apparently there's no way to do this directly. As a workaround, you could use the ajax.dataSrc option and/or the rowId option:
// Example using dataSrc option to manipulate data:
$("#example").dataTable({
ajax: {
url: "data.json",
dataSrc: function (json) {
for (var i = 0, ien = json.data.length; i < ien; i++) {
json.data[i][0] = 'View Message';
}
}
}
});
This worked for me:
$("#datatable").DataTable({
...
'createdRow': function(nRow, aData, iDataIndex) {
$(nRow).attr('id', 'row' + iDataIndex); // or if you prefer 'row' + aData.aid + aData.bid
},
...
});
I updated it from a question that seems to be a duplicate of this one. The createdRow callback is documented here.

View pictures or images inside Jquery DataTable

May I know if it is possible to put pictures or images into the rows of DataTables (http://datatables.net/) and how does one goes in doing it?
yes, simple way (Jquery Datatable)
<script>
$(document).ready(function () {
$('#example').dataTable({
"processing": true, // control the processing indicator.
"serverSide": true, // recommended to use serverSide when data is more than 10000 rows for performance reasons
"info": true, // control table information display field
"stateSave": true, //restore table state on page reload,
"lengthMenu": [[10, 20, 50, -1], [10, 20, 50, "All"]], // use the first inner array as the page length values and the second inner array as the displayed options
"ajax":{
"url": "#string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~"))/Home/AjaxGetJsonData",
"type": "GET"
},
"columns": [
{ "data": "Name", "orderable" : true },
{ "data": "Age", "orderable": false },
{ "data": "DoB", "orderable": true },
{
"render": function (data, type, JsonResultRow, meta) {
return '<img src="Content/Images/'+JsonResultRow.ImageSrcDB+'">';
}
}
],
"order": [[0, "asc"]]
});
});
</script>
[edit: note that the following code and explanation uses a previous DataTables API (1.9 and below?); it translates easily into the current API (in most cases, just ditch the Hungarian notation ("fnRowCallback" just becomes "rowCallback" for example) but I have not done so yet. The backwards compatibility is still in place I believe, but you should look for the newer conventions where possible]
Original reply follows:
What Daniel says is true, but doesn't necessarily say how it's done. And there are many ways. Here are the main ones:
1) The data source (server or otherwise) provides a complete image tag as part of the data set. Don't forget to escape any characters that need escaping for valid JSON
2) The data source provides one or more fields with the information required. For example, a field called "image link" just has the Images/PictureName.png part. Then in fnRowCallback you use this data to create an image tag.
"fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
var imgLink = aData['imageLink']; // if your JSON is 3D
// var imgLink = aData[4]; // where 4 is the zero-origin column for 2D
var imgTag = '<img src="' + imgLink + '"/>';
$('td:eq(4)', nRow).html(imgTag); // where 4 is the zero-origin visible column in the HTML
return nRow;
}
3) Similar to above, but instead of adding a whole tag, you just update a class that has the image as a background. You would do this for images that are repeated elements rather than one-off or unique pieces of data.
You mean an image inside a column of the table?
Yes, just place an html image tag
like this
<img src="Images/PictureName.png">
instead of putting data (some string) into a column just put the above html tag....
Asp.net core DataTables
The following code retrieve the image from a folder in WWWroot and the path in the DB field ImagePath
{
"data": "ImagePath",
"render": function (data) {
return '<img src="' + data + '" class="avatar" width="50" height="50"/>';
}
}
In case the Name of the picturefile is put together out of one or more informations in the table, like in my case:
src="/images/' + Nummer + Belegnummer + '.jpg"
you can make it that way:
var table = $('#Table').DataTable({
columnDefs: [
{
targets: 0,
render: getImg
}
]
});
function getImg(data, row, full) {
var Nummer = full[1];
var Belegnummer = full[4];
return '<img src="/images/' + Nummer + Belegnummer + '.jpg"/>';}
The picture is in the first column, so Targets = 0 and gets the Information from the same row.
It is necessary to add the parameters data and row.
It is not necessary to outsource it into a seperate function, here getImg, but it makes it easier to debug.