Here is my code:
const json = `{"BTC":{"available":0.00024868,"onOrder":0,"btcValue":0.00024868,"btcTotal":0.00024868},"LTC":{"available":0,"onOrder":0,"btcValue":0,"btcTotal":0},"ETH":{"available":0,"onOrder":0,"btcValue":0,"btcTotal":0},"NEO":{"available":0,"onOrder":0,"btcValue":0,"btcTotal":0},"BNB":{"available":0.08943066,"onOrder":0,"btcValue":0.0004663808919,"btcTotal":0.0004663808919}}`;
const data = JSON.parse(json);
var Table = require('cli-table');
const chalk = require("chalk");
const processed = Object.entries(data)
.filter(([, { available }]) => available > 0)
.map(([asset, { available, btcValue }]) => {
return { asset, available, btcValue };
});
const asArray = processed.map(Object.values);
//console.table(processed);
console.log(asArray);
console.log(asArray.length)
var table = new Table({
head: [chalk.green.bold('Coin'), chalk.green.bold('Available'), chalk.green.bold('BTC Value')]
, colWidths: [25, 25, 25]
});
table.push(
asArray[0], asArray[1]
);
var tableDisplay = table.toString();
console.log(tableDisplay);
I am trying to find a way of finding the asArray length, then putting each index in the table.push no matter how long it is,
So it would list asArray[0], and increase for each index of the array no matter what
the code currently works, but I have to know the length of the array in order to change the code each time.
you can set up a loop to loop through your asArray. A forEach loop will go through each of your element in the array until it reaches the end.
asArrays.forEach( element => {
table.push(element)
})
Related
I'm trying to reorder the items in an object so they will match the order of an array. The object is the row data and the array is the column names.
Here are the order of column names I want to match:
columnNames [
"System.Id",
"System.WorkItemType",
"System.Title",
"System.AssignedTo",
"System.State",
"Microsoft.VSTS.Common.ActivatedDate",
"System.ChangedDate",
"Microsoft.VSTS.Common.ClosedDate"
]
Here is an example of the object items I'm trying to reorder:
fields = {"System.Id":7993,"System.WorkItemType":"Task","System.State":"Closed","System.AssignedTo":"Jack Smith","System.ChangedDate":"2022-07-19T12:14:25.193Z","System.Title":"Update Dev Environment","Microsoft.VSTS.Common.ActivatedDate":"2022-07-19T12:13:49.713Z","Microsoft.VSTS.Common.ClosedDate":"2022-07-19T12:14:25.193Z"}
Ultimately I just want the values of each item in the object placed in an array which matches the order of the columnNames array, so like this:
rowArray = [
7993,"Task","Update Dev Environment","Jack Smith","Closed","2022-07-19T12:14:25.193Z","2022-07-19T12:13:49.713Z","2022-07-19T12:14:25.193Z"]
I get the fields object from calling an API. In order to get only the fields I want I loop through part of the API response.
Here's my entire code if it helps to see what I'm trying to do:
queryItems = result from API call
queryItems.forEach((item) => {
let sortingfields = [];
let sortedfields = [];
let rowArray = [];
//loop through fields part of each response item and add it to new array sortingfields
Object.entries(item.fields).forEach(([key, value]) => {
switch(key){
case 'System.AssignedTo':
sortingfields.push(key, item.fields[key].displayName);
break;
case 'System.ChangedDate':
const changedDate = item.fields[key].toLocaleString(DateTime.DATETIME_FULL);
sortingfields.push(key, changedDate);
break;
case 'Microsoft.VSTS.Common.ActivatedDate':
const activatedDate = item.fields[key].toLocaleString(DateTime.DATETIME_FULL);
sortingfields.push(key, activatedDate);
break;
case 'Microsoft.VSTS.Common.ClosedDate':
const closedDate = item.fields[key].toLocaleString(DateTime.DATETIME_FULL);
sortingfields.push(key, closedDate);
break;
default:
sortingfields.push(key, item.fields[key]);
break;
}
})
// sort array items so they match the order of the columnNames array
sortedfields = sortingfields.sort((entryA, entryB) => {
const [keyA] = entryA;
const [keyB] = entryB;
return columnNames.indexOf(keyA) - columnNames.indexOf(keyB)
})
// loop through sorted array and retrieve only the values
for (let s of sortedfields){
rowArray.push(sortedfields[s]);
}
// add array as a new row to Excel file using ExcelJS
let row = sheet.addRow(rowArray);
})
Now the error I get is Failure Exception: entryA is not iterable. I don't understand why given sortingfields is an array.
you can use map for that
you just map over the keys you want and get it from the object like this
const orderData = (data, order) => order.map(k => data[k])
const columnNames = [
"System.Id",
"System.WorkItemType",
"System.Title",
"System.AssignedTo",
"System.State",
"Microsoft.VSTS.Common.ActivatedDate",
"System.ChangedDate",
"Microsoft.VSTS.Common.ClosedDate"
]
const single = {"System.Id":7993,"System.WorkItemType":"Task","System.State":"Closed","System.AssignedTo":"Jack Smith","System.ChangedDate":"2022-07-19T12:14:25.193Z","System.Title":"Update Dev Environment","Microsoft.VSTS.Common.ActivatedDate":"2022-07-19T12:13:49.713Z","Microsoft.VSTS.Common.ClosedDate":"2022-07-19T12:14:25.193Z"}
const data = [{"System.Id":7993,"System.WorkItemType":"Task","System.State":"Closed","System.AssignedTo":"Jack Smith","System.ChangedDate":"2022-07-19T12:14:25.193Z","System.Title":"Update Dev Environment","Microsoft.VSTS.Common.ActivatedDate":"2022-07-19T12:13:49.713Z","Microsoft.VSTS.Common.ClosedDate":"2022-07-19T12:14:25.193Z"}, {"System.Id":7993,"System.WorkItemType":"Task","System.State":"Closed","System.AssignedTo":"Jack Smith","System.ChangedDate":"2022-07-19T12:14:25.193Z","System.Title":"Update Dev Environment","Microsoft.VSTS.Common.ActivatedDate":"2022-07-19T12:13:49.713Z","Microsoft.VSTS.Common.ClosedDate":"2022-07-19T12:14:25.193Z"}]
console.log(orderData(single, columnNames))
console.log(data.map(d => orderData(d, columnNames)))
I'm not really sure of what you want to achieve but here is my answer:
const fieldEntries = Object.entries(fields);
const entriesResult = fieldEntries.sort((entryA, entryB) => {
const [keyA] = entryA;
const [keyB] = entryB;
return columnNames.indexOf(keyA) - columnNames.indexOf(keyB)
})
const objResult = Object.fromEntries(entriesResult)
This gives the following value for objResult:
{
System.Id:7993
System.WorkItemType:"Task"
System.Title:"Update Dev Environment"
System.AssignedTo:"Jack Smith"
System.State:"Closed"
Microsoft.VSTS.Common.ActivatedDate:"2022-07-19T12:13:49.713Z"
System.ChangedDate:"2022-07-19T12:14:25.193Z"
Microsoft.VSTS.Common.ClosedDate:"2022-07-19T12:14:25.193Z"
}
Sandbox: https://playcode.io/932608
I've a table that I'm querying using findAndCountAll()
I'm basically using it for pagination/ordering using the order, limit and index properties. Something simple like this:
exports.getApplications = async(req, res, next) => {
const findOne = req.query.indexId;
const index = req.query.index || 0;
const limit = req.query.limit || 10;
const orderField = req.query.orderField || 'createdAt';
const orderDirection = req.query.orderDirection || 'DESC';
const order = [ orderField, orderDirection ];
const applications = await Application.findAndCountAll({
order: [order],
limit: parseInt(limit, 10),
offset: parseInt(index)
// ...
}
}
But I'd also like to be able to specify a particular indexId (findOne), and have that single entry appear before the paginated/ordered list (*edit: The row may or may not be included in the paginated list that's returned)
Is that possible using findAndCountAll? Or would I have to run a separate query using findByPk(), reduce the limit by 1, then unshift the entry to the front of the results array?
Thanks,
Nick
*Edit - Implementation in case anyone is interested. Now just have to make the queries run in parallel :)
try {
// Find a specific row to highlight if needed
if(findOne) {
topRow = await Application.findByPk(findOne, {
include: [...],
});
}
const applications = await Application.findAndCountAll({
include: [...],
order: [order],
limit: parseInt(limit, 10),
offset: parseInt(index)
});
// If there's a row to be highlighted
if(topRow) {
const visible = applications.rows.filter(application => application.id === topRow.id);
const index = applications.rows.findIndex(application => application.id === topRow.id);
// 1 too many results in the array
if(applications.rows.length + 1 > limit) {
// If the row appears in the array to be returned, remove it
if(visible) {
applications.rows.splice(index, 1);
} else {
// Remove the last element instead
applications.rows.pop();
}
} else {
if(visible) applications.rows.splice(index, 1);
}
// Add the highlighted topRow to the array
applications.rows.unshift(topRow)
}
Try to do this, assuming your findOne value is safe to use:
order: [
[sequelize.literal(`id = ${findOne}`), 'DESC'],
[orderField, orderDirection]
]
This will return true for the row you want, false for others, so it will first sort it to the top, then continue the rest of the ordering.
Building a script in google apps script.
I get values from an invoice data sheet with multiple lines per invoice so as to account for line items.
My progress so far has been to extract individual invoice numbers from the column (each invoice number occurs as many line items the individual invoice has).
The array todaysInvoices looks like this: [35033817, 35033818, 35033819, 35033820, 35033821]
Now, I need a way to create an object for each of these invoice numbers that has different properties (such as invoiceDate and customerName etc.). The initial invoice number as in the array should thereby be assigned as 'id' property to the new invoice object.
I need help to use objects in javascript.
If you require additional information, please let me know.
Below is a screenshot of a simplified version of my order sheet:
This is a clipping of my order sheet. Before and after the shown columns there are many more with more details but the hierarchies of information are already in the image
Below is the code I have so far:
const orderSheet = SpreadsheetApp.openById('SPREADSHEETID').getSheetByName('SHEETNAME');
const invoiceTemplate = DriveApp.getFileById('DOCUMENTID');
const tempFolder = DriveApp.getFolderById('FOLDERID');
const invoiceData = orderSheet.getRange(4,7, orderSheet.getLastRow() - 1, 57).getDisplayValues().filter(function (rows){ return rows[0] === 'INVOICED'});
const invDataRepo = SpreadsheetApp.openById('SPREADSHEETID2');
var timestamp = new Date();
function printBulkInvoices() {
logLineItems ();
var todaysInvoices = uniqueInvIDs ();
todaysInvoices.sort();
todaysInvoices.map(String);
//fetchInvData (todaysInvoices);
Logger.log (todaysInvoices)
}
function fetchInvData (invoiceIDs) {
let invoices = {
}
Logger.log(invoices)
invoiceIDs.forEach
}
function fetchLineItems (invoiceDataArray) {
}
// send array of todays unique invoice numbers (later all inv data?) to invdata sheet and log them
function logTodaysInvoices (invIDArr){
invIDArr.forEach
invDataRepo.getSheetByName('invdata').getRange(invDataRepo.getSheetByName('invdata').getLastRow()+1,1,invIDArr.length,1).setValue(invIDArr);
}
// return an array of unique invoice ids from todays invoice data
function uniqueInvIDs (){
let singleArray = invoiceData.map(row => row[5]);
let unique = [...new Set(singleArray)];
return unique;
}
//log incoicedata to invdatarepo-sheet 'lineitems'
function logLineItems (){
invDataRepo.getSheetByName('lineitems').getRange(invDataRepo.getSheetByName('lineitems').getLastRow()+1,2,invoiceData.length,invoiceData[0].length).setValues(invoiceData);
}
It's hard to say exactly what you need since we cannot see your Invoice Data Sheet.
But here's something that might give you a start:
let iobj = {idA:[]};
[35033817, 35033818, 35033819, 35033820, 35033821].forEach((id => {
if(!iobj.hasOwnProperty(id)) {
iobj[id]={date: invoiceDate, name: customName, items:[]};
iobj.idA.push(id);//I find it handy to have an array of object properties to loop through when I wish to reorganize the data after it's all collected
} else {
iobj[id].items.push({item info properties});//I am guessing here that you may wish to addition additional information about the items which are on the current invoice
}
});
Javascript Object
To follow up from your question:
Your loop to collect object data would start to look something like this:
function getInvoiceData() {
const ss = SpreadsheetApp.getActive();
const ish = ss.getSheetByName('Invoice Data');
const isr = 2;
const hA = ish.getRange(1, 1, 1, ish.getLastColumn()).getValues()[0];
let idx = {};//object return head index into row array based on header title which in this case I assume invoice number is labeled 'Invoicenumber'
hA.forEach((h, i) => {idx[h] = i});
const vs = ish.getRange(isr, 1, ish.getLastRow() - isr + 1, ish.getLastColumn()).getValues();
let iobj = { idA: [] };
vs.forEach(r => {
if (!iobj.hasOwnProperty(r[idx['invoicenumber']])) {
iobj[r[idx['invoicenumber']]] = { date: r[idx['invoicedate']], name: r[idx['customername']], items: [] };
iobj.idA.push(r[idx['invoicenumber']]);
} else {
iobj[r[idx['invoicenumber']]].items.push({ iteminfoproperties:'' });
}
});
}
I'm working on a project that is basically a e-store, with a bin. What I'm trying to do is send a POST request to the server with my items from the bin.
I have an empty array. I also have 3 Nodelists, I want to reach their values, textContent, dataset and nest all of them in my empty array.
This is the array:
var products = { Lines: [ ] };
What I've tried so far:
const prName = this.parentNode.parentNode.querySelectorAll('.product-name-link');
const prInput = this.parentNode.parentNode.querySelectorAll('.product-quantity-input');
const prPrice = this.parentNode.parentNode.querySelectorAll('.product-price-total');
prName.forEach(product => products.push(product.dataset.id))
prInput.forEach(product => products.Lines.push(product.value))
prPrice.forEach(product => products.Lines.push(product.textContent))
I want them to have a key=> value like in the example above.
The result that I need to get looks similar to this:
"Lines":[
{
"Assortiment":"1627aea5-8e0a-4371-9022-9b504344e724",
"Quantity":12678967.543233,
"SalePrice":12678967.543233,
"Warehouse":"1627aea5-8e0a-4371-9022-9b504344e724"
},
{
"Assortiment":"1627aea5-8e0a-4371-9022-9b504344e724",
"Quantity":12678967.543233,
"SalePrice":12678967.543233,
"Warehouse":"1627aea5-8e0a-4371-9022-9b504344e724"
},
{
"Assortiment":"1627aea5-8e0a-4371-9022-9b504344e724",
"Quantity":12678967.543233,
"SalePrice":12678967.543233,
"Warehouse":"1627aea5-8e0a-4371-9022-9b504344e724"
}
],
... with everything already said in my comments, a working solution might look like that ...
const products = { Lines: [] };
const prName = this.parentNode.parentNode.querySelectorAll('.product-name-link');
const prInput = this.parentNode.parentNode.querySelectorAll('.product-quantity-input');
const prPrice = this.parentNode.parentNode.querySelectorAll('.product-price-total');
// create a product item with each iteration step ...
prName.forEach((elmNode, idx) => products.Lines.push({
Assortiment: elmNode.dataset.id,
Quantity: prInput.item(idx).value,
SalePrice: prPrice.item(idx).textContent
}));
Wrap them with Array.from() as follows:
Array.from(elements).forEach( function(el) {
console.log(el);
});
I want to find strings that has data from the strings from the array 2 in the array1 and save result as separate uniq array.
As can you see I search for not exact values. From the array1 values I know only part of the information, and I want to find the complete strings, with that information, in array1. And at the end I want to save what I found. So, I don't have a problem with finding here, but a problem with saving in the valid single JSON.
Array examples:
Array #1:
{
"overflow": [
"id:address:name:location:email",
...
"id2:address2:name2:location2:email2"
]
}
Array #2:
[
"location:email",
...
"location2:email2"
]
Code:
resultArr: function() {
var arr1 = '/var/log/1.json';
var arr2 = '/var/log/2.json';
var arrResult = '/var/log/result.json';
var arr2Obj = JSON.parse(fs.readFileSync(arr2, 'utf-8'));
for (var i = 0; i < arr2Obj.length; i++) {
var arr1Obj = JSON.parse(fs.readFileSync(arr1, 'utf-8'));
arr1Obj.overflow = arr1Obj.overflow.filter(function(e) {
return e.includes(arr2Obj[i])
});
fs.appendFile(arrResult, JSON.stringify(arr1Obj, null, 2), 'utf-8');
}
}
My result:
[{
"overflow": [
"id:address:name:location:email"
]
}{
"overflow": [
"id54:address54:name54:location54:email56"
]
}{
"overflow": [
"id2:address2:name2:location2:email2",
"id6:address6:name6:location2:email2"
]
}
What I really want:
{
"overflow": [
"id:address:name:location:email",
"id54:address54:name54:location54:email56",
"id6:address6:name6:location2:email2",
"id2:address2:name2:location2:email2"
]
}
Instead of reading the file again and again, and appending to the result repeatedly, just do both actions only once. All the rest should happen in memory.
You will also get better results (no risk for duplicates in result) when you swap the loops: put the filter action as the outer loop. For the inner loop you can use some, since one match is enough for the entry to be included:
resultArr: function() {
var arr1 = '/var/log/1.json',
arr2 = '/var/log/2.json',
arrResult = '/var/log/result.json',
arr2Obj = JSON.parse(fs.readFileSync(arr2, 'utf-8')),
arr1Obj = JSON.parse(fs.readFileSync(arr1, 'utf-8'));
arr1Obj.overflow = arr1Obj.overflow.filter(function(e) {
return arr2Obj.some(function (f) {
return e.includes(f)
});
});
fs.writeFileSync(arrResult, JSON.stringify(arr1Obj, null, 2), 'utf-8');
}
At each iteration, you're creating a new object and appening it to a file.
JSON is not a good format to append to.
You're replacing the array instead of adding fields to it.
You can do it that way, it should work :
resultArr: () => {
let arr1 = '/var/log/1.json';
let arr2 = '/var/log/2.json';
let arrResult = '/var/log/result.json';
let arr2Obj = JSON.parse(fs.readFileSync(arr2, 'utf-8'));
let arr1Obj = JSON.parse(fs.readFileSync(arr1, 'utf-8')); // reading only one time
arr1Obj.overflow = arr2Obj.map(value => {
return arr1Obj.overflow.filter(e => return e.includes(value))
});
fs.writeFileSync(arrResult, JSON.stringify(arr1Obj, null, 2), 'utf-8'); //Writing only one time
}
Array.map() executes the closure for each field in your array and group all the values returned by the closure in another array.
I also replaced some keywords to make your code more ES6 compliant. I you really want to append, you should use CSV and not JSON.