Related
I want to get a range from my sheet. As recommended in Best practices, I am trying to get a array and manipulate it, but I'm confused:
const ss = Spreadsheet.getActive(),
sh = ss.getSheetByName("Sheet1"),
rg = sh.getRange("A1:C1"),//has 1,2,3
values = rg.getValues();
console.log(values);
The logs show
[[1,2,3]]
As you can see I got all three elements. But, when I log the length of the array(array.length), it is just 1(instead of 3). When I test existence of a element using .indexOf or .includes, It says -1 or false.
const values = /*same as logged above*/[[1,2,3]];
console.log(values.indexOf(2));//got -1 expected 1
console.log(values.includes(1));//got false expected true
Why?
I have the same issue with setValues().
rg.setValues([1,2,3]);//throws error
The error is
"The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues."
My specific Question is: What exactly does getValues() return? Is it a special kind of array?
Documentation excerpts:
From The official documentation, getValues() returns
a two-dimensional array of values,
It ALWAYS returns a two dimensional array of values.
One dimensional array is
[1,2,3]
Two dimensional array is
[[1,2,3]]
//or
[[1], [2], [3]]
There is/are array(s) inside a array.
indexed by row, then by column.
It is indexed by row first: i.e., The outer array has rows as inner array. Then each inner array has column elements. Consider the following simple spreadsheet:
A
B
C
1>
1
2
3
2>
2
3
4
3>
3
4
5
A1:A3 contains 3 rows and each row contains 1 column element. This is represented as [[1],[2],[3]]. Similarly, The following ranges represent the following arrays. Try to guess the array structure based on the A1 notation:
A1Notation
Number of Rows
Number of columns
Array Structure
array.length
array[0].length
A1:A3
3
1
[[1],[2],[3]]
3
1
A1:C1
1
3
[[1,2,3]]
1
3
A1:B2
2
2
[[1,2],[2,3]]
2
2
B1:C3
3
2
[[2,3],[3,4],[4,5]]
3
2
A2:C3
2
3
[[2,3,4],[3,4,5]]
2
3
Note how the two dimension provides direction.
See live visualization below:
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
const test = {
'A1:A3': [[1], [2], [3]],
'A1:C1': [[1, 2, 3]],
'A1:B2': [
[1, 2],
[2, 3],
],
'B1:C3': [
[2, 3],
[3, 4],
[4, 5],
],
'A2:C3': [
[2, 3, 4],
[3, 4, 5],
],
};
Object.entries(test).forEach(([key, value]) => {
console.log(`The range is ${key}`);
console.table(value);
console.info(`The above table's JavaScript array notation is ${JSON.stringify(value)}`)
console.log(`=================================`);
});
<!-- https://meta.stackoverflow.com/a/375985/ --> <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
The values may be of type Number, Boolean, Date, or String, depending on the value of the cell.
In the above example, We have Spreadsheet Number type elements converted to JavaScript number type. You can check spreadsheet type using =TYPE(). Corresponding JavaScript type reference is here
Empty cells are represented by an empty string in the array.
Check using
console.log(values[0][0]==="")//logs true if A1 is empty
Remember that while a range index starts at 1, 1, the JavaScript array is indexed from [0][0].
Given the two dimensional array structure, to access a value, two indexes of format array[row][column] is needed. In the above table, if A2:C3 is retrieved, To access C3, Use values[1][2]. [1] is second row in range A2:C3. Note that the range itself starts on second row. So, second row in the given range is row3 [2]is third column C.
Notes:
Warning:
Retrieved values from a range is always two dimensional regardless of the range height or width(even if it is just 1). getRange("A1").getValues() will represent [[1]]
setValues() will accept the same array structure corresponding to the range to set. If a 1D array is attempted, the error
The parameters (number[]/string[]) don't match the method signature for SpreadsheetApp.Range.setValues.
is thrown.
If the array does NOT exactly correspond to the range being set,i.e.,if each of the the inner array's length does not correspond to the number of columns in the range or the outer array's length does not correspond to the number of rows in the range being set, The error similar to the following is thrown:
The number of columns in the data does not match the number of columns in the range. The data has 5 but the range has 6.
Related answers to the above error:
https://stackoverflow.com/a/63770270
Related Search
indexOf/includes uses strict type checking. They won't work when you compare primitives against array objects. You can use Array.flat to flatten the 2D array to a 1D one. Alternatively, Use a plain old for-loop to check something.
const values = [[1,2,3]].flat();//flattened
console.log(values.indexOf(2));//expected 1
console.log(values.includes(1));//expected true
References:
Basic reading
MDN Arrays guide
I want to get a range from my sheet. As recommended in Best practices, I am trying to get a array and manipulate it, but I'm confused:
const ss = Spreadsheet.getActive(),
sh = ss.getSheetByName("Sheet1"),
rg = sh.getRange("A1:C1"),//has 1,2,3
values = rg.getValues();
console.log(values);
The logs show
[[1,2,3]]
As you can see I got all three elements. But, when I log the length of the array(array.length), it is just 1(instead of 3). When I test existence of a element using .indexOf or .includes, It says -1 or false.
const values = /*same as logged above*/[[1,2,3]];
console.log(values.indexOf(2));//got -1 expected 1
console.log(values.includes(1));//got false expected true
Why?
I have the same issue with setValues().
rg.setValues([1,2,3]);//throws error
The error is
"The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues."
My specific Question is: What exactly does getValues() return? Is it a special kind of array?
Documentation excerpts:
From The official documentation, getValues() returns
a two-dimensional array of values,
It ALWAYS returns a two dimensional array of values.
One dimensional array is
[1,2,3]
Two dimensional array is
[[1,2,3]]
//or
[[1], [2], [3]]
There is/are array(s) inside a array.
indexed by row, then by column.
It is indexed by row first: i.e., The outer array has rows as inner array. Then each inner array has column elements. Consider the following simple spreadsheet:
A
B
C
1>
1
2
3
2>
2
3
4
3>
3
4
5
A1:A3 contains 3 rows and each row contains 1 column element. This is represented as [[1],[2],[3]]. Similarly, The following ranges represent the following arrays. Try to guess the array structure based on the A1 notation:
A1Notation
Number of Rows
Number of columns
Array Structure
array.length
array[0].length
A1:A3
3
1
[[1],[2],[3]]
3
1
A1:C1
1
3
[[1,2,3]]
1
3
A1:B2
2
2
[[1,2],[2,3]]
2
2
B1:C3
3
2
[[2,3],[3,4],[4,5]]
3
2
A2:C3
2
3
[[2,3,4],[3,4,5]]
2
3
Note how the two dimension provides direction.
See live visualization below:
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
const test = {
'A1:A3': [[1], [2], [3]],
'A1:C1': [[1, 2, 3]],
'A1:B2': [
[1, 2],
[2, 3],
],
'B1:C3': [
[2, 3],
[3, 4],
[4, 5],
],
'A2:C3': [
[2, 3, 4],
[3, 4, 5],
],
};
Object.entries(test).forEach(([key, value]) => {
console.log(`The range is ${key}`);
console.table(value);
console.info(`The above table's JavaScript array notation is ${JSON.stringify(value)}`)
console.log(`=================================`);
});
<!-- https://meta.stackoverflow.com/a/375985/ --> <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
The values may be of type Number, Boolean, Date, or String, depending on the value of the cell.
In the above example, We have Spreadsheet Number type elements converted to JavaScript number type. You can check spreadsheet type using =TYPE(). Corresponding JavaScript type reference is here
Empty cells are represented by an empty string in the array.
Check using
console.log(values[0][0]==="")//logs true if A1 is empty
Remember that while a range index starts at 1, 1, the JavaScript array is indexed from [0][0].
Given the two dimensional array structure, to access a value, two indexes of format array[row][column] is needed. In the above table, if A2:C3 is retrieved, To access C3, Use values[1][2]. [1] is second row in range A2:C3. Note that the range itself starts on second row. So, second row in the given range is row3 [2]is third column C.
Notes:
Warning:
Retrieved values from a range is always two dimensional regardless of the range height or width(even if it is just 1). getRange("A1").getValues() will represent [[1]]
setValues() will accept the same array structure corresponding to the range to set. If a 1D array is attempted, the error
The parameters (number[]/string[]) don't match the method signature for SpreadsheetApp.Range.setValues.
is thrown.
If the array does NOT exactly correspond to the range being set,i.e.,if each of the the inner array's length does not correspond to the number of columns in the range or the outer array's length does not correspond to the number of rows in the range being set, The error similar to the following is thrown:
The number of columns in the data does not match the number of columns in the range. The data has 5 but the range has 6.
Related answers to the above error:
https://stackoverflow.com/a/63770270
Related Search
indexOf/includes uses strict type checking. They won't work when you compare primitives against array objects. You can use Array.flat to flatten the 2D array to a 1D one. Alternatively, Use a plain old for-loop to check something.
const values = [[1,2,3]].flat();//flattened
console.log(values.indexOf(2));//expected 1
console.log(values.includes(1));//expected true
References:
Basic reading
MDN Arrays guide
For example, you might have function with a complicated signature and varargs:
fun complicated(easy: Boolean = false, hard: Boolean = true, vararg numbers: Int)
It would make sense that you should be able to call this function like so:
complicated(numbers = 1, 2, 3, 4, 5)
Unfortunately the compiler doesn't allow this.
Is it possible to use named arguments for varargs? Are there any clever workarounds?
To pass a named argument to a vararg parameter, use the spread operator:
complicated(numbers = *intArrayOf(1, 2, 3, 4, 5))
It can be worked around by moving optional arguments after the vararg:
fun complicated(vararg numbers: Int, easy: Boolean = false, hard: Boolean = true) = {}
Then it can be called like this:
complicated(1, 2, 3, 4, 5)
complicated(1, 2, 3, hard = true)
complicated(1, easy = true)
Note that trailing optional params need to be always passed with name.
This won't compile:
complicated(1, 2, 3, 4, true, true) // compile error
Another option is to spare vararg sugar for explicit array param:
fun complicated(easy: Boolean = false, hard: Boolean = true, numbers: IntArray) = {}
complicated(numbers = intArrayOf(1, 2, 3, 4, 5))
Kotlin Docs says clearly that:
Variable number of arguments (Varargs)
A parameter of a function (normally the last one) may be marked with
vararg modifier:
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
allowing a variable number of arguments to be passed to the function:
val list = asList(1, 2, 3)
Inside a function a vararg-parameter of type T is visible as an
array of T, i.e. the ts variable in the example above has type
Array<out T>.
Only one parameter may be marked as vararg. If a vararg parameter
is not the last one in the list, values for the following parameters
can be passed using the named argument syntax, or, if the parameter
has a function type, by passing a lambda outside parentheses.
When we call a vararg-function, we can pass arguments one-by-one,
e.g. asList(1, 2, 3), or, if we already have an array and want to
pass its contents to the function, we use the spread operator
(prefix the array with *):
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
From: https://kotlinlang.org/docs/reference/functions.html#variable-number-of-arguments-varargs
To resume, you can make it using spread operator so it would look like:
complicated(numbers = *intArrayOf(1, 2, 3, 4, 5))
Hope it will help
The vararg parameter can be anywhere in the list of parameters. See example below of how it may be called with different set of parameters. BTW any call can also provide lambda after closed parenthesis.
fun varargs(
first: Double = 0.0,
second: String = "2nd",
vararg varargs: Int,
third: String = "3rd",
lambda: ()->Unit = {}
) {
...
}
fun main(args: Array<String>) {
val list = intArrayOf(1, 2, 3)
varargs(1.0, "...", *list, third="third")
varargs(1.0, "...", *list)
varargs(1.0, varargs= *list, third="third")
varargs(varargs= *list, third="third")
varargs(varargs= *list, third="third", second="...")
varargs(varargs= *list, second="...")
varargs(1.0, "...", 1, 2, 3, third="third")
varargs(1.0, "...", 1, 2, 3)
varargs(1.0)
varargs(1.0, "...", third="third")
varargs(1.0, third="third")
varargs(third="third")
}
I'm trying to use a PCRE regular expression to extract some JSON. I'm using a version of MariaDB which does not have JSON functions but does have REGEX functions.
My string is:
{"device_types":["smartphone"],"isps":["a","B"],"network_types":[],"countries":[],"category":["Jebb","Bush"],"carriers":[],"exclude_carriers":[]}
I want to grab the contents of category. I'd like a matching group that contains 2 items, Jebb and Bush (or however many items are in the array).
I've tried this pattern but it only matches the first occurrence: /(?<=category":\[).([^"]*).*?(?=\])/g
Does this match your needs? It should match the category array regardless of its size.
"category":(\[.*?\])
regex101 example
JSON not a regular language. Since it allows arbitrary embedding of balanced delimiters, it must be at least context-free.
For example, consider an array of arrays of arrays:
[ [ [ 1, 2], [2, 3] ] , [ [ 3, 4], [ 4, 5] ] ]
Clearly you couldn't parse that with true regular expressions.
See This Topic:
Regex for parsing single key: values out of JSON in Javascript
Maybe Helpful for you.
Using a set of non-capturing group you can extract a predefined json array
regex answer: (?:\"category\":)(?:\[)(.*)(?:\"\])
That expression extract "category":["Jebb","Bush"], so access the first group
to extract the array, sample java code:
Pattern pattern = Pattern.compile("(?:\"category\":)(?:\\[)(.*)(?:\"\\])");
String body = "{\"device_types\":[\"smartphone\"],\"isps\":[\"a\",\"B\"],\"network_types\":[],\"countries\":[],\"category\":[\"Jebb\",\"Bush\"],\"carriers\":[],\"exclude_carriers\":[]}";
Matcher matcher = pattern.matcher(body);
assertThat(matcher.find(), is(true));
String[] categories = matcher.group(1).replaceAll("\"","").split(",");
assertThat(categories.length, is(2));
assertThat(categories[0], is("Jebb"));
assertThat(categories[1], is("Bush"));
There are many ways. One sloppy way to do it is /([A-Z])\w+/g
Please try it on your console like
var data = '{"device_types":["smartphone"],"isps":["a","B"],"network_types":[],"countries":[],"category":["Jebb","Bush"],"carriers":[],"exclude_carriers":[]}',
res = [];
data.match(/([A-Z])\w+/g); // ["Jebb", "Bush"]
OK the above was pretty sloppy however a solid single regex solution to extract every single element regardless of the number, one by one and to place them in an array (res) is the following...
var rex = /[",]+(\w*)(?=[",\w]*"],"carriers)/g,
str = '{"device_types":["smartphone"],"isps":["a","B"],"network_types":[],"countries":[],"category":["Jebb","Bush","Donald","Trump"],"carriers":[],"exclude_carriers":[]}',
arr = [],
res = [];
while ((arr = rex.exec(str)) !== null) {
res.push(arr[1]); // <- ["Jebb", "Bush", "Donald", "Trump"]
}
Check it out # http://regexr.com/3d4ee
OK lets do it. I have come up with a devilish idea. If JS had look-behinds this could have been done simply by reversing the applied logic in the previous example where i had used a look-forward. Alas, there aren't... So i decided to turn the world the other way around. Check this out.
String.prototype.reverse = function(){
return this.split("").reverse().join("");
};
var rex = /[",]+(\w*)(?=[",\w]*"\[:"yrogetac)/g,
str = '{"device_types":["smartphone"],"isps":["a","B"],"network_types":[],"countries":[],"category":["Jebb","Bush","Donald","Trump"],"carriers":[],"exclude_carriers":[]}',
rev = str.reverse();
arr = [],
res = [];
while ((arr = rex.exec(rev)) !== null) {
res.push(arr[1].reverse()); // <- ["Trump", "Donald", "Bush", "Jebb"]
}
res.reverse(); // <- ["Jebb", "Bush", "Donald", "Trump"]
Just use your console to confirm.
In c++ you can do it like this
bool foundmatch = false;
try {
std::regex re("\"([a-zA-Z]+)\"*.:*.\\[[^\\]\r\n]+\\]");
foundmatch = std::regex_search(subject, re);
} catch (std::regex_error& e) {
// Syntax error in the regular expression
}
If the number of items in the array is limited (and manageable), you could define it with a finite number of optional items. Like this one with a maximum of 5 items:
"category":\["([^"]*)"(?:,"([^"]*)"(?:,"([^"]*)"(?:,"([^"]*)"(?:,"([^"]*)")?)?)?)?
regex101 example here.
Regards.
How do I insert an element at arbitrary position of Immutable.js List?
You are looking for the splicemethod:
Splice returns a new indexed Iterable by replacing a region of this Iterable with new values.
splice(index: number, removeNum: number, ...values: any[])
Where you can specify the index and if you write 0 as removeNum it will just insert the values at the given position:
var list = Immutable.List([1,2,3,4]);
console.log(list.toJS()); //[1, 2, 3, 4]
var inserted = list.splice(2,0,100);
console.log(list.toJS()); //[1, 2, 3, 4]
console.log(inserted.toJS()); //[1, 2, 100, 3, 4]
Demo JSFiddle.
It is worth pointing out that you can also use the insert method which is in fact synonymous with list.splice(index, 0, value) but it feels more intuitive and improves readability greatly.
const myImmutableList = Immutable.fromJS(['foo', 'baz'])
const newList = myImmutableList.insert(1, 'bar')
console.log(newList.toJS()) //["foo", "bar", "baz"]