I have a 2d array:
const arr = [ [ 'John Doe',
'john#something.com',
'Some text here',
'' ],
[ 'Steven Smith',
'steven#something.com',
'Another irrelevant text here',
'' ],
]
I want to find a row in the tab that matches the name in each array in this list and add a text value in the last column (where the '' is).
For example:
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const range = sheet.getDataRange()
const data = range.getValues()
const people = data.slice(1)
if a name in arr is found in people, add the text 'Found' in the last column in people for that name.
people looks like this:
[ [ 'John Doe',
'john#something.com',
'Some text here',
'' ] ]
I was able to find the match using this:
const peopleTarget = arr.map(person => people.find(el => el[0] === person[0]))
However, I need the row number to be able to set a value.
I know I need the getRange to be able to use setValue but I'm having difficulty grabbing the row number based on matching value between two arrays.
Any help is appreciated.
Create a set of names from arr, iterate over data and modify them in-place. Then setValues the modified array.
const arr = [
['John Doe', 'john#something.com', 'Some text here', ''],
[
'Steven Smith',
'steven#something.com',
'Another irrelevant text here',
'',
],
],
arrSet = new Set(arr.map((el) => el[0]));
data.forEach((people) =>
arrSet.has(people[0]) ? (people[3] = 'found') : null
);
range.setValues(data);
Related
The code below is my current code that works fine. It lets me import all data from one spreadsheet tab to another spreadsheet giving me the full range A:J. But now I do not want all data to be imported but I want to filter out specific stores (in sales data) based on their id number. The ID numbers are located in column C in source spreadsheet. I want to Filter for 8 different values in column C and import all data matching that condition into the targeted spreadsheet.
function importhistorical_sales_ee() {
//geth values to be imported from the source sheet
var values = SpreadsheetApp.openById('1mVECYu27lnOIFf7vHk1kI_55DID3APvWr_toWuZho14').
getSheetByName('Historical sales for SC planning -.csv').getRange('A:J').getValues();
//set values imported
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('test_ee');
target.getRange("A3:J").clearContent();
target.getRange(3,1,values.length,values[0].length).setValues(values);
}
I tested several options like the one below (but it is not working). I am expecting to be able to set the column I want to filter on and then also set which values I want to look for in that column.
Here is a simple example.
I have sample data in a spreadsheet like this.
I then filter the data for an array of 3 values as shown in the script. You can make it 8 or whatever you want. I prefer to use getDataRange() but you could use getRange(1,1,sheet.getLastRow(),9).getValues(). This ensures your not getting a bunch of blank rows.
Code.gs
function filterStores () {
try {
let wanted = [1,3,5];
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spread.getSheetByName("Sheet1");
let stores = sheet.getRange(1,1,sheet.getLastRow(),9).getValues();
let filtered = stores.filter( store => {
return wanted.some( id => store[2] === id );
}
);
let target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('test_ee');
target.getRange(3,1,target.getLastRow()-2,9).clearContent();
target.getRange(3,1,filtered.length,filtered[0].length).setValues(filtered);
console.log(filtered);
}
catch(err) {
console.log(err);
}
}
Execution log
9:16:03 AM Notice Execution started
9:16:05 AM Info [ [ 'a1', 'b1', 1, 'd1', 'e1', '', 'g1', '', '', 'j1' ],
[ 'a3', 'b3', 3, 'd3', 'e3', '', 'g3', '', '', 'j3' ],
[ 'a5', 'b5', 5, 'd5', 'e5', '', 'g5', '', '', 'j5' ],
[ 'a3', 'b3', 3, 'd3', 'e3', '', 'g3', '', '', 'j3' ] ]
9:16:05 AM Notice Execution completed
References
Array.filtere()
Array.some()
Arrow function
So I want to loop a column Text in a Google sheet and assign values to columns main_category, item_category and item based on the value of Text which maps to an array of dictionaries for each rule applied. I'm struggling at setting up the nested loop for the dictionary.
Sheet structure / source data:
Text main_category item_category item
-------------------------------------------------
Banana
Tomato
Choco
Desired outcome:
Text main_category item_category item
-------------------------------------------------
Banana Fruits Yellow Banana
Tomato Vegetables Red Tomato
Choco Sweets Brown Choco
My approach:
function process_actuals() {
// Initiating an array of dicts
var rules_array = [
{contains: 'Bana', main_category: 'Fruits', item_category: 'Yellow', item: 'Banana'},
{contains: 'Tomato', main_category: 'Vegetables', item_category: 'Red', item: 'Tomato'},
{contains: 'Choco', main_category: 'Sweets', item_category: 'Brown', item: 'Choco'},
];
// Get active Spreadsheet
var s = SpreadsheetApp.getActiveSpreadsheet();
// Get sheet "Source"
var sht = s.getSheetByName('Source')
// Get the range where data is present in sht
var drng = sht.getDataRange();
// Get the range size
var rng = sht.getRange(2,1, drng.getLastRow(),drng.getLastColumn());
// Get an array of values within rng
var rngA = rng.getValues();
// Set up loop
for (var i = 2; i < rngA.length; i++) {
// Get the search string
let search_cell = sht.getRange(i, 1)
search_string = search_cell.getValue()
// Loop array of dicts and check if search string is within "contains" key
for (var dict in rules_array) {
// If search string is in contains key, populate row
if (search_string in dict['contains']) { // breaks here
// Populate rows
var target_cell = sht.getRange([i][2]) // main_category
target_cell.setValue(dict['main_category'])
}
}
}
}
I receive the error TypeError: Cannot use 'in' operator to search for 'Banana' in undefined.
Modification points:
When your rules_array is used, dict of for (var dict in rules_array) { is 0, 1, 2,,,. I thought that this might be the reason for your issue. This has already been mentioned in the comments.
When setValue is used in the loop, the process cost will become high.
When these points are reflected in a sample script, how about the following sample script?
Sample script:
function process_actuals() {
var rules_array = [
{ contains: 'Bana', main_category: 'Fruits', item_category: 'Yellow', item: 'Banana' },
{ contains: 'Tomato', main_category: 'Vegetables', item_category: 'Red', item: 'Tomato' },
{ contains: 'Choco', main_category: 'Sweets', item_category: 'Brown', item: 'Choco' },
];
// 1. Retrieve values from "Source" sheet.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Source');
var [[,...header], ...values] = sheet.getDataRange().getValues();
// 2. Create an array for putting to the sheet.
var v = values.map(([a]) => {
var t = rules_array.filter(o => a.includes(o.contains));
return t.length == 1 ? header.map(h => t[0][h]) : Array(3).fill("");
});
// 3. Put the array to "Source" sheet.
sheet.getRange(2, 2, v.length, v[0].length).setValues(v);
}
In this sample script, an array for putting to Spreadsheet is created using the retrieved values and rules_array, and put it to the sheet of Source.
Note:
In this sample script, from your question, it supposes that the header row of your script is Text, main_category, item_category, item. Please be careful about this.
Reference:
map()
Added:
From your following replying,
However when I log v it is empty, thus the macro doesn't populate anything. Do I have to further enrich your sample script before? Re process cost, we aretalking about a couple of thousand rows only
In this case, I'm worried that your Spreadsheet might not have the header row. If my understanding is correct, how about the following sample script?
Sample script:
function process_actuals() {
var rules_array = [
{ contains: 'Bana', main_category: 'Fruits', item_category: 'Yellow', item: 'Banana' },
{ contains: 'Tomato', main_category: 'Vegetables', item_category: 'Red', item: 'Tomato' },
{ contains: 'Choco', main_category: 'Sweets', item_category: 'Brown', item: 'Choco' },
];
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Source')
var values = sheet.getDataRange().getValues();
var header = ["main_category", "item_category", "item"];
var v = values.map(([a]) => {
var t = rules_array.filter(o => a.includes(o.contains));
return t.length == 1 ? header.map(h => t[0][h]) : Array(3).fill("");
});
sheet.getRange(1, 2, v.length, v[0].length).setValues(v);
}
In this sample script, it supposes that your Spreadsheet has no header row. And, the order of values is given by var header = ["main_category", "item_category", "item"];.
This question already has answers here:
Merge 2 arrays of objects
(46 answers)
Closed 1 year ago.
Say I have two data arrays for a ticketed event. One is attendees:
[
{name: 'Jack', ticket_code: 'iGh4rT'},
{name: 'Lisa', ticket_code: 'it1ErB'}
]
The other is tickets:
[
{code: 'iGh4rT', name: 'General Admission'},
{code: 'it1ErB', name: 'VIP'}
]
Now say I want to display a table like this:
Name
Ticket Name
Jack
General Admission
Lisa
VIP
I am struggling with doing this efficiently. I can display a table with one array no problem like something like this:
for (let i = 0; i < attendees.length; i++){
const row = `<tr>
<td>${attendees[i].name}</td>
<td>${attendees[i].ticket_code}</td>
</tr>`
document.getElementById('TableBody').innerHTML += row
I need to somehow 'query' the tickets array with the code from the attendees array for that particular person, get the name of the ticket, and supplant the ticket name instead of the code.
With SQL something like this is easy, but is one able to "query" an array and get a specific property? Should I construct a whole new array with the needed info? What is the best way to do this that would work for large unordered datasets?
You could take one of your array as an object with code as key for the object and map the other array with wanted data and the previous stored data from the object.
const
attendees = [{ name: 'Jack', ticket_code: 'iGh4rT' }, { name: 'Lisa', ticket_code: 'it1ErB' }],
tickets = [{ code: 'iGh4rT', name: 'General Admission' }, { code: 'it1ErB', name: 'VIP' }],
ticketsByCode = Object.fromEntries(tickets.map(o => [o.code, o])),
table = attendees.map(({ name, ticket_code }) => [name, ticketsByCode [ticket_code].name]);
console.log(table);
try this:
let a = [
{name: 'Jack', ticket_code: 'iGh4rT'},
{name: 'Lisa', ticket_code: 'it1ErB'}
];
let b = [
{code: 'iGh4rT', name: 'General Admission'},
{code: 'it1ErB', name: 'VIP'}
];
let c = b.map(item => {
return {
tiketName: item.name,
...a.find(itemA => itemA.ticket_code == item.code)
}
});
console.log(c);
I'm new to node.js and javascript. I have the following array:
var oldarray = [
'name1\tstreet\tperson\tphone1\tphone2\nname2\street2\tperson1\tphone82\tphone3\n'
]
Note, this is a single element array. First, I require the array to contain a new element after each new line first, then, re-format like below:
let headers = {
name: "",
street: "",
person: "",
phone 1 "",
phone 2 ""
}
How can I parse through each element (after creating a new element after each +), and assign an object within an array after each instance of \
The desired output is this:
[{
name: 'name1',
street: 'street2',
person: 'person1',
phone1: 'phone82 ',
phone2: 'phone3'
},
{
name: 'name2',
street: 'street2',
person: 'person1',
phone1: 'phone1 ',
phone2: 'phone2'
}]
Any help is highly appreciated.
If you have the same structure for all items in OLD_ARRAY you can use map, filter and reduce in order to manipulate your input.
So what I did?
In case that you have multiple strings like the example input (more than 1 array item) I convert it to sub-arrays of each string by using map and split by \n, which is your string separator. Than I filtered it by strings that are not empty (becasue that you have a post-fix of \n as well).
From each sub-array I extracted all the contacts using extractContacts function - it splites the sub-array by your separaotr, \t, and map it according to your contacts temaplte.
Since it's a format of array of arrays, I used reduce to concat all the arrays together
const OLD_ARRAY = [
'name1\tstreet\tperson\tphone1\tphone2\n' +
'name2\tstreet2\tperson1\tphone82\tphone3\n'
];
function extractContacts(templates) {
return templates.map(t => t.split('\t'))
.map(details => ({
name: details[0],
street: details[1],
person: details[2],
phone1: details[3],
phone2: details[4]
}));
}
let contacts = OLD_ARRAY.map(str => str.split('\n').filter(str => str !== ''))
.map(template => extractContacts(template))
.reduce((a, acc) => acc.concat(a), []);
console.log(contacts)
You can split each oldarray value on \n and then \t into newarray, and then use Object.fromEntries to build an object from each newarray value, combining the split values with each key from headers:
var oldarray = [
'name1\tstreet\tperson\tphone1\tphone2\n' +
'name2\tstreet2\tperson1\tphone82\tphone3\n'
]
let newarray = [];
oldarray.forEach(s => s.trim().split('\n').map(v => newarray.push(v.split('\t'))));
let headers = {
'name': "",
'street': "",
'person': "",
'phone 1': "",
'phone 2': ""
}
let keys = Object.keys(headers);
out = newarray.map(s => Object.fromEntries(s.map((v, i) => [keys[i], v])));
console.log(out);
First split the array by \n to get individual paths and then split them by \t, and use reduce to create new header objects from each subarray
var oldarray = [
'name1\tstreet\tperson\tphone1\tphone2\n' +
'name2\tstreet2\tperson1\tphone82\tphone3\n' +
'name4\tstreet4\tperson4\tphone84\tphone4\n'
]
arr = oldarray.flatMap(o => o.split("\n"))
c = arr.map(o => o.split("\t"))
c.pop()
result = c.reduce((acc,[name, street, person, phone1, phone2],i) => {
acc = [...acc,{name:name,street:street,person:person,phone1:phone1,phone2:phone2}]
return acc
},[])
console.log(result)
This question already has answers here:
Accessing an object property with a dynamically-computed name
(19 answers)
Closed 5 years ago.
I have a hand-typed database with an object that has categories and a list of words for each category, as below:
var words =
{
sports: [
'baseball', 'football', 'volleyball', 'basketball', 'soccer'],
animals: [
'dog', 'cat', 'elephant', 'crocodile', 'bird'],
entertainment: [
'netflix', 'movies', 'music', 'concert', 'band', 'computer']
}
My HTML has a bootstrap dropdown that will display all categories based on that list. I have the code working to give me the value of the category clicked as a string: as below:
$(document).on('click', '.dropdown-menu li a', function () {
var selectedCategory;
selectedCategory = $(this).text();
//setting value of category to global variable
categorySelected = selectedCategory;
});
I need to be able to find the key in my database from that value.
The problem is that I can't access words."animals"
I need to take the quotation marks off my string to get the list of words like this:
words.animals
How do I do this? I've tried replace() but it doesn't work.
It seems like you're trying to access the list of values corresponding to the category in your words object. Keys can be strings, so words['animals'] would be an example of getting your list of animals.
JavaScript allows variables to be used as keys as well, so you can access it as follows:
words[categorySelected]
You can pass the text(selected value from drop down) to a function to find the key
var words = {
sports: [
'baseball', 'football', 'volleyball', 'basketball', 'soccer'
],
animals: [
'dog', 'cat', 'elephant', 'crocodile', 'bird'
],
entertainment: [
'netflix', 'movies', 'music', 'concert', 'band', 'computer'
]
}
// function to find the key
function findKey(selText) {
//loop through the object
for (var keys in words) {
//get the array
var getArray = words[keys]
//inside each array check if the selected text is present using index of
if (getArray.indexOf(selText) !== -1) {
console.log(keys)
}
}
}
findKey('music')