How to create a ragged array in Chapel - arrays

I'm attempting to create and "array of arrays" all of different lengths. For instance
A = [[1 2 5 ]
[3 5]
[2 6 9]]
With Chapel strongly typed, I don't know how to declare and populate A. My first use of this will be to iterate over A and find a set of unique elements, as in
[1 2 3 5 6 9]
So it would be useful to see an example of construction and use. Thanks!

We call this feature "skyline" arrays or "jagged" arrays. While Chapel currently does not implement it directly, it is easy to do it with records. For example, each element of the outer array can be a record carrying the corresponding inner array and its domain:
record InnerArray {
var dom: domain(1); // starts out empty
var arr: [dom] int;
// Optional function: when printing an InnerArray, do not show the domain.
proc writeThis(c: channel) { c.write("["); c.write(arr); c.write("]"); }
}
proc initElem(ref dest: InnerArray, src: []) {
dest.dom = src.domain;
dest.arr = src;
}
var A: [1..3] InnerArray;
initElem(A[1], [1,2,5]);
initElem(A[2], [3,5]);
initElem(A[3], [2,6,9]);
// The default printout invokes writeThis on each element of A.
writeln(A);
// To iterate over all inner elements, need nested loops.
// If appropriate, either/both can be "forall".
for outer in A do
for inner in outer.arr do
writeln(inner);

Related

Error in swift playground: Cannot assign value of type 'Int' to type '[Int]'

var number: [Int] = [1,2,3,4]
var newArray: [Int] = []
for i in 0...number.count-1{
newArray = number[i] * number[i+1]
}
print(newArray)
I want output like this: [1 * 2, 2 * 3, 3 * 4].
I just don't get it where is the problem...
var number: [Int] = [1,2,3,4]
let things = zip(number, number.dropFirst()).map(*)
Whenever you need to turn something like [1, 2, 3, 4] into pairs (1, 2), (2, 3) etc, then the AdjacentPairs method is useful - in Swift Algorithms package - https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/AdjacentPairs.swift
Or you can zip a collection with its dropFirst for the same result.
And whenever you need to turn an [A]s into an [B]s then map with a function that turns As into Bs. So in this example you want to turn an array of tuples of Int, like [(1,2), (2,3), (3,4)] into array of Int, like [2, 6, 12] by multiplying the 2 Ints together, so map with *
The benefit of writing it this way is you would avoid the issues with your array mutation, getting index values wrong, running off the ends of arrays etc, and it's often easier to read and think about if you express it without the indices and assignments.
The problem that the compiler flags you is that you assign a single Int value to an array of Int. The following line will resolve that immediate issue:
newArray.append(number[i] * number[i+1])
This should pass compilation but then create a runtime error at execution. The reason is that when you try to fetch number[i+1] when i == number.count-1, you actually fetch number[number.count]. This entry does not exist with 0-based indices. To get 3 sums out of 4 array entries, your loop should iterate 3 times:
for i in 0 ..< number.count-1 {
Or, if you prefer closed ranges:
for i in 0 ... number.count-2 {
A more Swifty way would be to use map, as #Dris suggested. The return type for map is implicitly given by the result of the multiplication, so you can write:
let newArray = number.indices.dropLast().map { i in
number[i] * number[i+1]
}
You can use map()
let numbers = [1,2,3,4]
let newArray = numbers.enumerated().map { $1 * numbers[($0 + 1) % numbers.count] }
May be you should not loop to count-1 but stop before and add result to array :
for i in 0..<number.count-1 {
newArray.append(number[i] * number[i+1])
}

What does the range method getValues() return and setValues() accept?

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

What does the following Go array operation do?

I am confused about how the following code works, especially what is the purpose of "..."
array = append(array[:i], array[i+1:]...)
The line
a = append(a[:i], a[i+1:]...)
creates a new slice by removing the item at position i in a, by combining the items from 0 to i (not included), and from i+1 to the end.
Your second question is what is the purpose of .... append accepts a slice as first argument, and an unlimited number of arguments, all with a type assignable to the type of its elements.
append is defined as
func append(slice []Type, elems ...Type) []Type
Writing
a = append(a[:i], a[i+1:]...)
is equivalent as writing
a = append(a[:i], a[i+1], a[i+2], a[i+3], a[i+4]) //and so on, until the end of the slice.
Using a[i+1:]... is basically a shorthand syntax, as the Go spec describes in https://golang.org/ref/spec#Passing_arguments_to_..._parameters:
If f is variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T. If f is invoked with no actual arguments for p, the value passed to p is nil. Otherwise, the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T
Playground
array = append(array[:i], array[i+1:]...)
is removing an element at index i
but another thing to point out is that slice is backed by an underlying array. For example:
package main
import (
"fmt"
)
func main() {
myArray := [6]int {1,2,3,4,5,6}
mySlice := myArray[:]
fmt.Println("myArray before append: ", myArray)
i := 3
mySlice = append(mySlice[:i], mySlice[i+1:]...)
fmt.Println("mySlice after append: ", mySlice)
fmt.Println("myArray after append: ", myArray)
}
Result:
myArray before append: [1 2 3 4 5 6]
mySlice after append: [1 2 3 5 6]
myArray after append: [1 2 3 5 6 6]
goplayground
In the underlying [1,2,3] stayed in place, that data never go moved anywhere, while [5,6] which were given by b[i+1] were appended to [1,2,3], and thus overwrote [3,4]; the other [6] stayed in place.
Even though you get different copy of a slice the underlying array will be the same*, this makes append a much more efficient operation then if the whole underlying array had to be copied over!
*If underlying array exceeds it's capacity, a new larger array will be allocated and values from old array would be copied to the new array, but this will never happen when removing an element.
Built-in func append is Variadic Function.
To pass slice argument to any variadic function, you have to use ...
Go lang spec: Passing arguments to ... parameters
If f is variadic with final parameter type ...T, then within the
function the argument is equivalent to a parameter of type []T. At
each call of f, the argument passed to the final parameter is a new
slice of type []T whose successive elements are the actual arguments,
which all must be assignable to the type T. The length of the slice is
therefore the number of arguments bound to the final parameter and may
differ for each call site.
This line would give you a result value removing position i.
array = append(array[:i], array[i+1:]...)
Let's say, we have
array := []int{1, 2, 3, 4, 5, 6, 7}
i := 3
fmt.Println("Original slice:", array)
part1 := array[:i]
part2 := array[i+1:]
fmt.Println("part1:", part1)
fmt.Println("part2:", part2)
array = append(array[:i], array[i+1:]...)
fmt.Println("Result slice:", array)
Output:
Original slice: [1 2 3 4 5 6 7]
part1: [1 2 3]
part2: [5 6 7]
Result slice: [1 2 3 5 6 7]
Play Link: https://play.golang.org/p/_cIk0VcD6w
The purpose of ... is to save you typing individual elements as the append method takes first argument as slice and then variable number of arguments for elements to be appended.
i.e. You actually need to call append as
append(sliceName[:i], array[i+1], array[i+2], array[i+3], array[i+4])
but to avoid typing long list of elements, you can simply use ... after the slice or array to spread it as individual elements to be passed as arguments.

swift getting an array from something like array[0..<10]

I want to get a range of objects from an array. Something like this:
var array = [1,3,9,6,3,4,7,4,9]
var newArray = array[1...3] //[3,9,6]
The above would access elements from index 1 to 3.
Also this:
newArray = array[1,5,3] // [3,4,6] would be cool
This would retrieve elements from index 1, 5 and 3 respectively.
That last example can be achieved using PermutationGenerator:
let array = [1,3,9,6,3,4,7,4,9]
let perms = PermutationGenerator(elements: array, indices: [1,5,3])
// perms is now a sequence of the values in array at indices 1, 5 and 3:
for x in perms {
// iterate over x = 3, 4 and 6
}
If you really need an array (just the sequence may be enough for your purposes) you can pass it into Array's init method that takes a sequence:
let newArray = Array(perms)
// newArray is now [3, 4, 6]
For your first example - with arrays, that will work as-is. But it looks from your comments like you're trying it with strings as well. Strings in Swift are not random-access (for reasons relating to unicode). So you can't use integers, they have an String-specific bidirectional index type:
let s = "Hello, I must be going"
if let i = find(s, "I") {
// prints "I must be going"
println(s[i..<s.endIndex])
}
This works :
var n = 4
var newArray = array[0..<n]
In any case in
Slicing Arrays in Swift you'll find a very nice sample of the Python slice using a extension to Arrays in Swift.

Non-empty size information for cell arrays

In MATLAB I would like to keep a list of polylines - containing vertices (x,y) - in a container and I thought the best idea is to use cell arrays for this task. Each line would be represented in a row in a cell array, with vertices (x,y) being the elements of the cells. The different lines would be of different length, that's why I thought it would be a good idea to use cell arrays.
My problem however is that I don't know how can I append to the first non-empty element of each row in a cell-array?
Here is an example:
cell{1,1} = 1
cell{2,1} = 2
cell{3,1} = 3
cell{2,2} = 4
cell{2,3} = 5
cell =
[1] [] []
[2] [4] [5]
[3] [] []
For example now I want to append a new element to the end of row 1, and another one to row 2. How do I know what is the first position where I can append a new element?
Or shell I use cell arrays inside cell arrays for this task?
How would you implement a container for a list of polylines MATLAB?
This is a bad way to store your data, for the very problems you're encountering. A couple notes:
The first column is used as an index (i.e. 1 for polyline 1, 2 for polyline 2, etc.), which is unnecessary since that info is already stored implicitly in the structure of your data.
With this method, points will have to be stacked next to each other, which will be a nightmare for indexing.
With each x and y in a different cell, it's going to be an unneeded hassle to plot/store even a single point.
There are 2 good ways to store all this information.
Cell array: Like Clement pointed out, this is nice and simple, and will let you stack different points in the same polyline along a second dimension.
celldata = {[] [4 5] []};
celldata{2} = [celldata{2}; 1 1];
celldata{3} = [0.5 0.5];
>> celldata
celldata =
[] [2x2 double] [1x2 double]
Structure array: This is a nice way to go if you want to store polyline-level metadata along with your points.
strucdata = struct('points', {[] [4 5] []}, 'info', {'blah', 'blah', 'blah'});
strucdata(2).points = [strucdata(2).points; 1 1];
strucdata(3).points = [0.5 0.5];
>> strucdata
strucdata =
1x3 struct array with fields:
points
info
>> strucdata(2)
ans =
points: [2x2 double]
info: 'blah'
To answer your first question, you can use this:
n=1;
length([cell{n,:}])+1
n=2;
length([cell{n,:}])+1
With [...] you treat the cell slice as an array and not several scalar values.

Resources