update docs

This commit is contained in:
2026-06-27 16:00:31 +07:00
parent f33f4f0790
commit e3d09e6ebd
2 changed files with 356 additions and 98 deletions
+355 -97
View File
@@ -5,7 +5,7 @@ export noNegative!, randomWithProb, randomChoiceWithProb, findIndex, limitvalue,
replaceLessThan, replaceBetween, cartesianAssign!, sumAlongDim3, matMul3Dto3DmanyTo1batch,
matMul_3Dto4D_batchwise, isNotEqual, linearToCartesian, vectorMax, findMax,
multiply_last, multiplyRandomElements, replaceElements, replaceElements!, isBetween,
isLess, allTrue, getStringBetweenCharacters, JSON3read_stringKey, mkDictPath!,
isLess, allTrue, getStringBetweenCharacters, mkDictPath!,
getDictPath, detectKeywordVariation, textToDict, dictify, ordereddictify
using JSON, DataStructures, Distributions, Random, Dates, UUIDs, DataFrames, CSV
@@ -152,12 +152,32 @@ end
""" read_textfile_by_index(folder_path::String, read_file_number::Integer=1)
with multiple text file in a folder,
this function read x_th text file in a folder (filename is sorted by OS)
# What it does
- Reads the x-th text file from a folder, where files are listed by the OS
without explicit sorting. Returns the file number, filename, and content.
# Example
utils.read_textfile_by_index(cleaned_data_path, 2)
read 2nd txt file in a folder
# Arguments
- `folder_path::String`
Path to the folder containing text files.
- `read_file_number::Integer=1`
Which file to read (1-based index). Defaults to the first file.
# Return
- A tuple of `(file_number::Integer, filename::String, content)` where:
- `file_number` is the index of the file that was read
- `filename` is the actual filename string
- `content` is a vector of lines from the file (or `nothing` if error)
# Notes
- Errors if `folder_path` is not a valid directory.
- Errors if `read_file_number` exceeds the number of files in the folder.
# Examples
```jldoctest
julia> using GeneralUtils
julia> result = read_textfile_by_index("/path/to/folder", 2)
(2, "sample.txt", ["line1", "line2", ...])
```
"""
function read_textfile_by_index(folder_path::String, read_file_number::Integer=1)
if isdir(folder_path)
@@ -453,6 +473,34 @@ function randomChoiceOnTarget(target::AbstractVector, choiceList::AbstractVector
return randomChoiceOnTarget.(target, 1, (choiceList,), (probability,))
end
""" Compute the linearly weighted average of an array.
# What it does
- Assigns weights proportional to position indices (1, 2, 3, ...) to array
elements and returns the weighted average.
# Arguments
- `a::Array`
Array of numeric values. Elements must support multiplication with numbers
and summation.
# Return
- The linearly weighted average as a floating-point number.
# Formula
For an array `a` with `n` elements, computes:
```
sum(i * a[i]) / sum(a) for i = 1 to n
```
# Examples
```jldoctest
julia> using GeneralUtils
julia> a = [10, 20, 30]
julia> linearly_weighted_avg(a)
23.333333333333332
```
"""
function linearly_weighted_avg(a::Array)
total = 0.0
for (i, v) in enumerate(a)
@@ -463,10 +511,36 @@ function linearly_weighted_avg(a::Array)
end
""" Convert String that is holded inside a variable to Symbol
# Example
x = "hello" # x is a variable holding String "hello" \n
y = variable_to_symbol(x) # y holds :hello
""" Convert a variable's value (String) into a Symbol.
# What it does
- Takes a variable containing a String value and converts it to a Symbol
using Julia's expression interpolation mechanism.
# Arguments
- `variable`
Any variable whose value is a String. The function uses `string(variable)`
internally to obtain the value.
# Return
- A `Symbol` constructed from the string value of the input variable.
# Notes
- This function uses `:$variable` interpolation to capture the variable's
value as a Symbol. It works with any variable type that can be converted
to String.
# Examples
```jldoctest
julia> using GeneralUtils
julia> x = "hello"
julia> variable_str_to_symbol(x)
:hello
julia> y = "world_test"
julia> variable_str_to_symbol(y)
:world_test
```
"""
function variable_str_to_symbol(variable)
semi = :($variable)
@@ -510,6 +584,45 @@ function fieldname_useable_type(somestruct, fieldname::Symbol;
end
""" Draw unique elements from a list without replacement.
# What it does
- Randomly selects a specified number of distinct elements from a collection,
optionally excluding certain elements from consideration. Uses in-place
shuffling for efficiency.
# Arguments
- `drawOptions::Array`
Collection of elements to draw from.
- `draw_number::Integer`
Number of unique elements to draw.
# Keyword Arguments
- `exclude_list::Union{AbstractArray,Nothing}=nothing`
Elements to exclude from the drawing pool. If `nothing`, no elements are
excluded.
# Return
- An array of `draw_number` unique elements drawn from `drawOptions`, excluding
any elements in `exclude_list`.
# Notes
- The function copies `drawOptions` and shuffles in-place, then pops elements
sequentially to ensure uniqueness.
- Errors if `draw_number` exceeds the number of available elements after
exclusion.
# Examples
```jldoctest
julia> using GeneralUtils
julia> options = [1, 2, 3, 4, 5]
julia> randomNoRepeat(options, 3)
[3, 1, 5]
julia> randomNoRepeat(options, 2; exclude_list=[1, 5])
[4, 2]
```
"""
function randomNoRepeat(drawOptions::Array, draw_number::Integer;
exclude_list::Union{AbstractArray,Nothing}=nothing)
draw_option = copy(drawOptions)
@@ -676,22 +789,49 @@ function selectRange(d::Dict{Symbol, <:AbstractVector}, range)
return newDict
end
""" Assign value to a given Dict by array of keys
""" assignDict!(dict::Dict, accessArray::Array{Symbol}, valueToAssign)
# Example
d = Dict(
:a1=> Dict(:c=> 5),
:a2=> Dict(
:k=> 10,
:b=> Dict(
:s=> "target",
)
)
)
index = [:a2, :b, :s] \n
assignDict!(d, [:a2, :b, :s], "wow")
# What it does
- Recursively traverses a nested dictionary structure using a vector of keys
and assigns a value to the final key. Creates intermediate dictionaries
if they don't exist.
return 1 if no target key in a given dict.
# Arguments
- `dict::Dict`
The root dictionary to traverse and modify.
- `accessArray::Array{Symbol}`
A vector of symbols representing the key path to traverse.
- `valueToAssign`
The value to assign at the final key in the path.
# Return
- `0` on success (value assigned)
- `1` if the path cannot be traversed (missing intermediate keys)
# Notes
- The function walks through each key in `accessArray` except the last one,
expecting intermediate keys to already exist in the dictionary.
- If any intermediate key is missing, the function returns `1` without
modifying the dictionary.
- The final key in `accessArray` receives the `valueToAssign`.
# Examples
```jldoctest
julia> using GeneralUtils
julia> d = Dict(
:a1=> Dict(:c=> 5),
:a2=> Dict(
:k=> 10,
:b=> Dict(
:s=> "target",
)
)
)
julia> assignDict!(d, [:a2, :b, :s], "wow")
0
julia> d[:a2][:b][:s]
"wow"
```
"""
function assignDict!(dict::Dict, accessArray::Array{Symbol}, valueToAssign)
wd = nothing
@@ -712,9 +852,42 @@ function assignDict!(dict::Dict, accessArray::Array{Symbol}, valueToAssign)
end
end
""" convert hour(0-23), minute(0-59) into julia time object
# Example
time
""" iTime(h::Integer, m::Integer)
# What it does
- Converts hour (0-23) and minute (0-59) into a Julia `Time` object using
12-hour format with AM/PM indicator.
# Arguments
- `h::Integer`
Hour in 24-hour format (0 to 23).
- `m::Integer`
Minute (0 to 59).
# Return
- A `Time` object representing the time in 12-hour format with AM/PM.
# Notes
- Hours 0 and 12 are special cases: 0 becomes 12 AM, 12 becomes 12 PM.
- Hours 1-11 remain the same with "am" suffix.
- Hours 13-23 are converted to 1-11 with "pm" suffix.
- Minutes less than 10 are zero-padded.
# Examples
```jldoctest
julia> using GeneralUtils
julia> iTime(0, 30)
12:30 AM
julia> iTime(9, 15)
9:15 AM
julia> iTime(12, 0)
12:00 PM
julia> iTime(14, 5)
2:05 PM
```
"""
function iTime(h::Integer, m::Integer)
if h == 0
@@ -758,19 +931,40 @@ function limitvalue(v::Number, lowerbound::Pair, upperbound::Pair)
end
""" Assign matrix b to matrix a according to matrix b's CartesianIndex.
""" cartesianAssign!(a, b)
Arguments:\n
a : target matrix.
b : source matrix.
Return:\n
Resulting matrix a.
# What it does
- Assigns elements from matrix `b` to matrix `a` using the Cartesian indices
of `b`. Elements are copied in the order they appear when iterating over `b`,
and placed into `a` at the corresponding Cartesian positions of `b`.
Example:\n
```jldoctest
julia> not done yet
```
# Arguments
- `a`
Target matrix where values from `b` will be assigned.
- `b`
Source matrix whose Cartesian indices determine where values are placed in `a`.
# Return
- `nothing`
# Notes
- The function iterates through `b` in column-major order (Julia's default),
retrieving each element's Cartesian index and assigning it to the same
position in `a`.
- Matrix `a` must have sufficient size to accommodate all Cartesian indices
from `b`; otherwise, an `BoundsError` may occur.
# Examples
```jldoctest
julia> using GeneralUtils
julia> a = zeros(4, 4);
julia> b = [1 2; 3 4];
julia> cartesianAssign!(a, b);
julia> a[1:2, 1:2]
2×2 Matrix{Float64}:
1.0 3.0
2.0 4.0
```
"""
function cartesianAssign!(a, b)
for (i, v) in enumerate(b)
@@ -1125,7 +1319,6 @@ julia> text = "{\"ask\": {\"text\": \"Could you please tell me about the special
julia> GeneralUtils.getStringBetweenCharacters(text, '{', '}', endCharLocation="end")
"{\"ask\": {\"text\": \"Could you please tell me about the special event?\"\n}}"
```
# Signature
"""
function getStringBetweenCharacters(text::T, startChar::Char, endChar::Char;
endCharLocation::String="next", includeChar::Bool=true)::String where {T<:AbstractString}
@@ -1157,59 +1350,43 @@ function getStringBetweenCharacters(text::T, startChar::Char, endChar::Char;
end
""" mkDictPath!(dict::Union{Dict{Symbol, Any}, Dict{String, Any}}, addkeys::Union{Vector{String}, Vector{Symbol}}, value)
""" Read JSON string and return a dictionary with string key. (JSON3 defaults to symbol key)
This function solve the problem of reading JSON with string key.
Arguments:
jsonString::String
Return:
a dictionary with string key
Example:
```jldoctest
julia> jsonString = {\"wine type\": \"Red\", \"intensity level\": \"medium-bodied\"}
julia> JSON3read_stringKey(jsonString)
Dict{String, Any} with 2 entries:
"intensity level" => "medium-bodied"
"wine type" => "Red"
```
"""
function JSON3read_stringKey(jsonString::AbstractString)
jsonobj = JSON3.read(jsonString)
newDict = OrderedDict{String,Any}()
for (k,v) in jsonobj
newDict[string(k)] = v
end
return newDict
end
""" Create nested dict path if it does not already exist. The same concept as Julia's mkpath()
# What it does
- Recursively creates nested dictionary paths if they do not exist and assigns
a value to the final key. Similar to `mkpath()` but for dictionaries.
# Arguments
- `dict::Dict`
target dict
- `addkeys::Union{Vector{String}, Vector{Symbol}}`
keys to be added to dict
- `value`
value to be added to dict at final key in keypath
- `dict::Union{Dict{Symbol, Any}, Dict{String, Any}}`
The target dictionary to traverse and modify. Must use consistent key types
(either all `String` or all `Symbol`).
- `addkeys::Union{Vector{String}, Vector{Symbol}}`
A vector of keys representing the path to traverse. Intermediate dictionaries
are created if they don't exist.
- `value`
The value to assign at the final key in the path.
# Return
- dict with added keypath
- The assigned `value`.
# Example
# Notes
- The function ensures key type consistency: the type of keys being added must
match the type of existing keys in the dictionary.
- Intermediate dictionaries are automatically created with the appropriate key
type when they don't exist.
- The function walks through each key in `addkeys` except the last one,
creating intermediate dictionaries as needed, and assigns `value` to the final
key.
# Examples
```jldoctest
julia> using Revise
julia> using GeneralUtils
julia> d = Dict{String, Any}("a" => Dict{String, Any}("b" => 10))
julia> GeneralUtils.mkDictPath!(d, ["a", "v", "x", "y", "z"], 42)
Dict{String, Any} with 1 entry:
"path" => Dict{Any, Any}("to"=>Dict{Any, Any}("nested"=>Dict{Any, Any}("value"=>42)))
julia> d = Dict("a" => Dict("b" => 10))
julia> mkDictPath!(d, ["a", "v", "x", "y", "z"], 42)
42
julia> d["a"]["v"]["x"]["y"]["z"]
42
```
# Signature
"""
function mkDictPath!(dict::Union{Dict{Symbol, Any}, Dict{String, Any}},
addkeys::Union{Vector{String}, Vector{Symbol}}, value)
@@ -1233,27 +1410,36 @@ function mkDictPath!(dict::Union{Dict{Symbol, Any}, Dict{String, Any}},
end
""" Get nested dict value using a vector of keys
""" getDictPath(dict::Dict, keys::Vector)
# What it does
- Retrieves a value from a nested dictionary by traversing a vector of keys.
Creates intermediate dictionaries if they don't exist.
# Arguments
- `dict::Dict`
target dict
- `keys::Vector`
keys vector
- `dict::Dict`
The root dictionary to traverse.
- `keys::Vector`
A vector of keys representing the path to traverse. Each key in the vector
is used to access the next level of nesting.
# Return
- dict with added keypath
- The value at the final key in the path.
# Example
# Notes
- Errors with `ArgumentError` if any intermediate key is missing from the
dictionary path.
- The function walks through each key in `keys` except the last one,
expecting intermediate keys to exist in the dictionary.
- The final key in `keys` is used to retrieve the value.
# Examples
```jldoctest
julia> using Revise
julia> using GeneralUtils
julia> d = Dict{Symbol, Any}(:a => Dict{Symbol, Any}(:b => 10))
julia> GeneralUtils.getDictPath(d, [:a, :b])
julia> d = Dict(:a => Dict(:b => 10))
julia> getDictPath(d, [:a, :b])
10
```
# Signature
"""
function getDictPath(dict::Dict, keys::Vector)
current_dict = dict
@@ -1405,8 +1591,6 @@ OrderedCollections.OrderedDict{Any, Any}(:thought => "what to do",
:plan => "wake up and going out",
:action => "1. wake up 2. eat 3. sleep")
```
# Signature
"""
function textToDict(text::String, detectKeywords::Vector{String};
dictKey::Union{Vector{String}, Nothing}=nothing,
@@ -1467,6 +1651,80 @@ function textToDict(text::String, detectKeywords::Vector{String};
return od2
end
""" Recursively convert dictionary into an HTML string representation.
# What it does
- Walks a nested `AbstractDict` structure and produces a well-formed HTML string
where each dictionary key becomes an HTML tag. Nested dictionaries become
nested tags, and scalar values (numbers, strings, etc.) become the text
content of leaf tags.
# Arguments
- `d::AbstractDict`
The dictionary to convert. Keys must be strings or symbols that form valid
HTML tag names.
# Keyword Arguments
- `indent_level::Integer=1`
Initial indentation level for the output. Each recursive level increases
indentation by one.
- `indent_str::String=" "`
String used for each indentation level.
# Return
- A single HTML string representing the dictionary structure, with proper
opening and closing tags and appropriate indentation.
# Notes
- Keys are sorted alphabetically for deterministic output.
- Works recursively: dictionary values produce nested tags; non-dictionary
values are placed as text between opening/closing tags.
# Examples
```jldoctest
julia> d = Dict(
"html" => Dict(
"head" => Dict("title" => "Test"),
"body" => Dict(
"h1" => "Hello",
"p" => "World"
)
)
);
julia> println(dict_to_string_html(d))
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Hello</h1>
<p>World</p>
</body>
</html>
```
"""
function dict_to_string_html(d::AbstractDict; indent_level=1, indent_str=" ")
lines = String[]
padding = indent_str ^ indent_level
# Sort keys for predictable, clean output
for k in sort(collect(keys(d)))
v = d[k]
if v isa AbstractDict
# Open tag, recurse for children, then close tag
push!(lines, "$padding<$k>")
ind_level = indent_level + 1
push!(lines, dict_to_string_html(v; indent_level=ind_level, indent_str=indent_str))
push!(lines, "$padding</$k>")
else
# Leaf node: put key and value on a single line
push!(lines, "$padding<$k>$v</$k>")
end
end
return join(lines, "\n")
end