# lib_2da function library

Dependencies: lib_anon, lib_array, lib_fn, lib_ini, lib_sfo, lib_sugar, lib_tools, lib_ui

## Description

Functions for manipulating 2d arrays via their (assumed uppercase) rows and columns. The main use case is reading these 2d arrays out of .2da game files and extracting data from them, and/or manipulating them and writing them back in. This is (hopefully) a much simpler and more transparent way to manipulate 2das than by READ_2DA_ENTRY and friends.

Functions here should engage with the general 2da format and shouldn't refer to specific features of particular game's 2das. They should start with "2da_".

This library can handle these file formats:

1. Standard, legally-formatted 2da files.
2. IDS files, which are read in with an added set of row headers which are integers labelled from 0, and with columns 'int' and 'symbol'.
3. Tables with column headers, which are read in with an added set of row headers as for IDS files.

## Public functions

2da_clone_column(silent:b, array:a, clone_to:s, location:s, clone_from:s)=(array:a) dimorphic

Given a 2d array, a column label 'clone_from', and a new column label 'clone_to', insert a copy of the clone_from column with the clone_to label, in the position specified by 'location'. 'location' can be 'first' (or 'start'), 'last' (or 'end'), 'before column_label' or 'after column_label'. If we can't find the column label, we default to 'last' (and whine about it unless silent=1).

2da_clone_row(silent:b, array:a, clone_to:s, location:s, clone_from:s)=(array:a) dimorphic

Given a 2d array, a row label 'clone_from', and a new row label 'clone_to', insert a copy of the clone_from row with the clone_to label, in the position specified by 'location'. 'location' can be 'first' (or 'start'), 'last' (or 'end'), 'before row_label' or 'after row_label'. If we can't find the row label, we default to 'last' (and whine about it unless silent=1).

2da_column_to_array(silent:b, column:s, array:a)=(array_out:a) dimorphic

Given a 2d array and column label 'column', extract that column as a 1d array indexed by the row labels.

If we can't find the column label, we return an empty array (and whine about it unless silent=1).

2da_delete_column(array:a, column:s)=(array:a) dimorphic

Given a 2d array, and column label 'column', delete that column.

2da_delete_row(array:a, row:s, lookup_column:s)=(array:a) dimorphic

Given a 2d array, and row label 'row', delete that row.

2da_extract_array(silent:b, domain:s, range:s, array:a, keymap:f, map:f, case:[upper|lower|mixed])=(array:a) dimorphic

Given a 2d array, and column labels 'domain' and 'range' for that array, return a 1d array whose keys are the elements of the 'domain' column and whose values are the elements of the 'range' column.

If you leave either 'domain' or 'range' empty, the row headers are used instead. If we can't find domain or range, we return an empty array (and whine unless silent=1).

Optionally, you can specify functions 'keymap' and/or 'map', which are applied to the keys and values respectively before being put into the output array. You can use the anonymous function construct.

2da_find_default()=(value:s) patch

In the current file, return the default 2da entry, or "NOT_2DA" if the file is not a 2da file.

2da_fix(apply_to_file:b=1)=(legal:s, fixed_file:s) patch

Turns the current file (assumed to be a 2da file) into a legal 2da file with no incomplete entries, by

1. forcing the first line to be '2DA V1.0'
2. removing any entry beyond the first on the second line
3. truncating any entries that lie beyond the range defined by the columns on the third line
4. filling in any incomplete entries with the array's default character.

Returns 'legal', which is 1 or 0, and 'fixed_file', which is just a string containing the full contents of the file. Optional argument: 'apply_to_file' (can be 1 or 0, default is 1); if set to 1, the changes are actually carried out on the file being patched; if set to 0, they're discarded

2da_inject_array(silent:b, force_uppercase:b=1, array:a, array_in:a, column:s, row:s)=(array:a) dimorphic

Given a 2d array, a column header of that array, and a k=>v array whose keys are row headers in the 2d array, inject the array elements into the 2d array, as (k=>v) goes to (k,col,v).

If force_uppercase=1 (default), array_in's keys are uppercased.

2da_insert_column(silent:b, array:a, column:s, location:s, entry:s="-1")=(array:a) dimorphic

Given a 2d array, and a column label, insert a new column with that column label in the position specified by 'location'. 'location' can be 'first' (or 'start'), 'last' (or 'end'), 'before column_label' or 'after column_label'. If we can't find the column label, we default to 'last' (and whine about it unless silent=1).

The new rows are filled with 'entry'.

2da_insert_row(silent:b, array:a, row:s, location:s, entry:s="-1")=(array:a) dimorphic

Given a 2d array, and a row label, insert a new row with that row label in the position specified by 'location'. 'location' can be 'first' (or 'start'), 'last' (or 'end'), 'before row_label' or 'after row_label'. If we can't find the row label, we default to 'last' (and whine about it unless silent=1).

The new rows are filled with 'entry'.

2da_make(rows:a, columns:a, fill:s="*")=(array:a) dimorphic

Given two arrays in k=>_ format, make a 2da with each character filled with some fixed data

2da_prettier_print()=() patch

Like PRETTY_PRINT_2DA, but checks the file has >2 rows and doesn't treat "2DA V1.0" as two columns

2da_process_table(inline:i, 2da:i, table:s, path:s, location:s, locbase:s, function:s)=() action

'table' should be a header table whose headers are the arguments of the action function 'function'. (INT_VAR arguments should be indicated with ':i'.) Each row of the table is fed to the function.

Alternately, if INT_VAR 2da is set to 1, 'table' can be a 2da. It is treated the same way, except that each row name is fed to the function as the STR_VAR 'rowname'

2da_read(silent:b, reflect:b, remove_comments:b=1, type:[2da|ids|table_header|table_no_header], rowmap:f, colmap:f, rowname_column:s, case:[upper|lower|mixed])=(default:s, columns:a, rows:a, array:a) patch
2da_read(silent:b, inline:b, reflect:b, file:s, case:[upper|lower|mixed], path:s, type:s, location:s, locbase:s, rowmap:f, colmap:f, rowname_column:s)=(default:s, value:s, columns:a, rows:a, array:a) action

Read a 2da file (or, in patch context, the current 2da file) into a 2d array. Also return an array of uppercased row headers and column headers, in the format row_label=>row_number. ('case' controls the case of the row and column headers; it's uppercase by default on genuine 2das, mixed by default otherwise). The default value is also returned, as 'default'. (On non-2da files, * is returned)

If you don't specify a path for the 2da file, it's assumed to be a game file.

In action context, if the file doesn't exist return value=0; otherwise, return value=1. Also whine if it doesn't exist, unless silent=1.

If the file is a 2da, and 'reflect' is set, reverse rows and columns. If it's a 2da, and "rowname_column" is set, use that column (if it's present) for the row names instead of the usual entries. In the latter case, we add a new 'ROWNUMBER' column containing column zero. (This is not compatible with 'reflect'.)s

If 'rowmap' and/or 'colmap' are set, they get applied to the row and column entries before the array is constructed.

If the file contains WEIDU-style // comments, they are removed. (You can override this by setting remove_comments to 0.)

2da_renumber(start_at:i, array:a)=(array:a) dimorphic

Given a 2d array, replace its row labels with sequential integers starting at 'start_at' (i.e. 0 by default)

2da_row_to_array(silent:b, row:s, array:a)=(array_out:a) dimorphic

Given a 2d array and row label 'row', extract that row as a 1d array indexed by the column labels.

If we can't find the row label, we return an empty array (and whine about it unless silent=1).

2da_sort(rows:s, columns:s, array:s)=(array:a) dimorphic

Takes a 2da, sorts rows and/or columns. Set "rows" and/or "columns" to "lexicographically" or "numerically" (case-insensitive), or else feed it a function to use in array_sort. Can use 'l' or 'n' as synonyms

2da_to_3da(column1:s, column2:s, array:a)=(array_out:a) dimorphic

Given a 2d array and two column headers, extract a 3d array where the first two keys are the values in the new columns and the third is the old column header.

e.g. if row 14 has col1=x, col2=y, then 3da(x,y,z)=2da(14,z).

2da_to_ini(2da:s, ini:s, path:s, location:s, locbase:s, ini_path:s, default:s="*", ignores:s, section_key:s)=() action

2da_write(reflect:b, silent:i, number_rows:b, array:a, type:[2da|ids|table_header|table_noheader], default:s, case:[m="mixed"ixed|upper|lower])=() patch
2da_write(number_rows:b, reflect:b, file:s, path:s, location:s, locbase:s, type:[2da|ids|table_header|table_noheader], array:a, default:s)=() action

Write a 2d array into a 2da file (or, in patch context, the current 2da file).

If you don't specify a path for the 2da file, it's assumed to be a game file.

If 'number_rows' is set to 1, the row names are replaced by integers, counting upwards from 0. If 'reflect' is set to 1, rows and columns are swapped.

If you don't specify the default element, we try to read it from the current file