It seems that the Excel JS-API does shallow copies of Range.values. If I want to read a range from one place and write differently modified copies of it to 2 different places I need to use a deep copy of the range: how do I do that?
This code still does a shallow copy:
async function setValue() {
try {
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getActiveWorksheet();
// A1 contains 9876543210
let rng1 = sheet.getRange("a1").load("values");
await context.sync();
console.log(JSON.stringify(rng1.values));
// rng1 value is correct =9876543210
let rng2 = sheet.getRange("B4");
let rng3 = sheet.getRange("B6");
let avar = [[]];
for (var j = 0; j < rng1.values.length; j++) {
for (var k = 0; k < rng1.values[0].length; k++) {
avar[j][k] = rng1.values[j][k];
}
}
rng3.values = avar;
console.log(JSON.stringify(avar));
// rng2 value is correct =9876543210
let avar2 = avar[0][0];
rng3.values[0][0] =avar2 + 0.01;
console.log(JSON.stringify(rng3.values));
// rng3 value is correct =9876543210.01
rng2.values = avar;
console.log("Show values of the 3 ranges before Sync")
console.log(JSON.stringify(rng1.values));
console.log(JSON.stringify(rng2.values));
console.log(JSON.stringify(rng3.values));
// rng2 and rng3 both show as 987654321.01
// BUT ONLY Rng3 has been altered!
await context.sync();
});
console.log("Done!");
}
catch (error) {
OfficeHelpers.Utilities.log(error);
}
}
To answer my own question - you can make a deep copy using JQuery extend
let avar = [];
$.extend(true, avar, rng1.values);
rng3.values = avar;
Related
I'm trying to retrieve an array of cards for a project. However, in my function, the final contacts array returns an empty array.
I know that, because I have an async call to another funcion inside the forEach loop, the loop doesn't execute as intended. However, I'm very newbie when it comes to deal with this issues, so I want to ask you what's the best approach to deal with this.
This is my code:
export const extractsIDSForUser = async (currentUser: User) : Promise <Object> => {
let contactCards = currentUser.contacts;
const contacts = [];
const usersRef = await firebase.firestore().collection('Users').get();
const usersSnapshot = usersRef.docs.map(doc => doc.data());
contactCards.forEach(async folder => {
const ids = [];
folder.forEach(contact => {
ids.push(contact);
});
for (let i = 0; i < ids.length; i +=1) {
const contact = ids[i];
for (let j = 0; j < usersSnapshot.length; j += 1) {
const userId = usersSnapshot[j].id;
// Async call to function
const cardsFromUser = await extractCardsFromUser(userId);
const arrayCards = Object.values(cardsFromUser);
if (arrayCards.length > 0) {
for (let j = 0; j < arrayCards.length; j += 1) {
const arrayId = arrayCards[j].id;
const sameCardId = arrayId === contact;
if (sameCardId) {
// Where I insert the values into the array
contacts.push(arrayCards[j]);
}
}
}
}
}
});
// this is empty
return contacts;
}
What will be the best approach to deal with this?
I think you have already found a solution, but I had a similar problem and found this article quite helpful.
You could use a traditional for (const contactCard of contactCards) and it will work, but it will be less efficient than using a Promise.all approach.
Also trying to get that custom code that extracts the months from the string with regex in my code snippet. I believe I am close but not quite. Console log is returning "undefined" values for the key/value pairs and 0 for the months when it should return 60. Any thoughts on how to restructure this would be very much appreciated! I am trying to get the highest number of months/years from an array and set it to a property in HubSpot. Thank you kindly for any advice on how to properly configure to get correct values.
hubspotClient.crm.lineItems.batchApi.read({
inputs,
properties: ['hs_recurring_billing_period', 'recurringbillingfrequency',]
})
.then(res => {
const inputs = res.body.results.map(result => {
result.properties.recurringbillingfrequency =
result.properties.recurringbillingfrequency;
result.properties.months = Number(result.properties.months);
return { term: hs_recurring_billing_period, frequency: recurringbillingfrequency };
})
console.log(inputs);
let term = 0;
const largestNum = (years) => {
//let term = 0;
for (let i=0; i <res.body.results.length; i++){
let { recurringbillingfrequency, hs_recurring_billing_period } =
res.body.results[i].properties;
console.log(recurringbillingfrequency, hs_recurring_billing_period)
if(recurringbillingfrequency = "Annually")
{
let months = Number(hs_recurring_billing_period.replace(/\D/g, ''));
let years = months / 12;
// let term = 0;
if (years[i] > term) {
term = years[i];
}
}
}
return term;
}
console.log(largestNum(term));
return;
The map function looks strange to me:
const inputs = res.body.results.map(result => {
result.properties.recurringbillingfrequency = result.properties.recurringbillingfrequency;
result.properties.months = Number(result.properties.months);
return { term: hs_recurring_billing_period, frequency: recurringbillingfrequency };
})
within the scope of the mapping function, recurringbillingfrequency and hs_recurring_billing_period in the return object are not defined. Would it work by replacing the return value with as so?
return {
hs_recurring_billing_period: result.properties.hs_recurring_billing_period,
recurringbillingfrequency: result.properties.recurringbillingfrequency
};
Also, I am not quite sure how this line is necessary:
result.properties.recurringbillingfrequency = result.properties.recurringbillingfrequency;
So either this loop will work and extract the months and set to years or you can use Lodash with one line of code.
let term = 0;
for (let i=0; i <inputs.length; i++){
let { recurringbillingfrequency, hs_recurring_billing_period } =
inputs[i];
console.log(recurringbillingfrequency, hs_recurring_billing_period)
if(recurringbillingfrequency.toLowerCase() === 'annually')
{
let months = hs_recurring_billing_period;
let years = months / 12.0;
/*
let highest = 0;
function getHighestTerm(values) {
for (let j=0; j < values.length; j++)
if (j === 0) {
highest = values;
} else if (highest > values[j]) {
highest = values[j];
}
return highest;
}
*/
term = _.max(_.map(inputs, 'hs_recurring_billing_period')) / 12.0;
Wondering if it is possible to use a loop to add dynamically named objects to an array, so I don't need to repeat the "push" on an array. Tks !!
let _objA0 = { "name":"regionId", "value":"myRegion" };
let _objA1 = { "name":"vdcId", "value":"myId" };
let _objA2 ... _objA100
let test = []
test.push(_objA0)
test.push(_objA1)
...
test.push(_objA100)
I guess it's the right time to use eval
let test = [];
for(let i = 0; i <= 100; i++) {
test.push(eval(`_objA${i}`));
}
You can access variables (with var keyword) by window object , try this:
var _objA0 = { "name":"regionId", "value":"myRegion" };
var _objA1 = { "name":"vdcId", "value":"myId" };
let test = [];
for(let i = 0; i < 2; i++){
test.push(window['_objA' + i]);
}
console.log(test)
The goal is to break an object of unknown length and shape into small objects 3 elements each. Only vanilla JS solution; I don't want to use _.pick etc.
Example of large object:
const data = {
someFirstKey: 'someFirstVal',
someSecondKey: 'someSecondVal',
...
someLastKey: 'someLastVal'
}
Desired chunk with 3 keys:
{someKey0: 'someVal0', someKey1: 'someVal1', someKey2, 'someVal2'}
Based on the comments, it seems like you are actually looking for a way to split an object up into several smaller objects. I would approach that like this:
const data = {a:1,b:2,c:3,d:4,e:5,f:6,g:7};
const chunk_size = 3, chunks = [];
for ( const cols = Object.entries( data ); cols.length; )
chunks.push( cols.splice(0, chunk_size).reduce( (o,[k,v])=>(o[k]=v,o), {}));
console.log( chunks );
Ok, so this might not be the most efficient way, but it works on my box. ;)
const data = {} // fill in your data here
const keys = Object.keys(data); // gets you the keys from your obj.
const numberOfWholeParts = Math.floor( keys.length / 3 ); // or whatever your size limit is
const remainder = keys.length % 2; // keys left after all whole parts are filled
const arrayOfParts = [];
for(let i=0;i<numberOfWholeParts;i++) {
const obj = {};
const keySegment = keys.slice(i*3, i*3+3);
for(let j=0; j<3; j++) {
obj[keySegment[j]] = data[keySegment[j]];
}
arrayOfParts.push(obj);
}
if(remainder > 0){
const obj = {};
let remainingKeys = keys.slice(-remainder)
for(let i=0; i<remainingKeys.length;i++) {
obj[remainingKeys[i]] = data[remainingKeys[i]];
}
arrayOfParts.push(obj);
}
Using Object.fromEntries:
const entries = Object.entries(data);
const grouped = [];
for (let i = 0; i < entries.length; i++) {
if (i % groupSize === 0) {
grouped.push([entries[i]]);
} else {
grouped[Math.floor(i / groupSize)].push(entries[i]);
}
}
const chunks = grouped.map(o => Object.fromEntries(o));
Using Object destructuring
const { someKey0, someKey1, someKey2 } = data
const newData = {
someKey0,
someKey1,
someKey2
}
I am trying to list all data from Javascript keys Object when I put it in console log there is all information, but when I want to use InnerHTML I keep getting the first object only shown.
function gotData(data){
var scores = data.val();
var keys = Object.keys(scores);
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
var pro = scores[k].result;
var doc = document.getElementById("result").innerHTML = pro;
}
}
In this case, it will give me only a result of first element from my Firebase
Thanks
Please check out this stackblitz-demo, looks like your missing one small thing if I am understanding what your expected outcome is.
onClick() {
const scores = [{
'one': 1
}, {
'two': 2
}, {
'three': 3
}, {
'four': 4
}, {
'five': 5
}];
var keys = Object.keys(scores);
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
const pro = scores[k].result;
// here the += is what i think you're missing.
const doc = document.getElementById("example").innerHTML += k;
}
}
The issue is that you are overriding innerHTML each time. Instead, you need to append to the existing innerHTML. Change the last line to...
const doc = document.getElementById("example").appendChild(document.createTextNode(k))
appendChild is also much faster than setting innerHTML
.hasOwnProperty is how to see just your stored values. Does this help?
d = snap.val();
for (var k in d) {
if (d.hasOwnProperty(k)) {
if (isObject(d[k]){
console.log(k, d[k]);
} else {
console.log (k);
}
}
}
function isObject(obj) {
return obj === Object(obj);
}