Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

Commit

Permalink
Partial Dictionary reimplementations and new Dictionary methods
Browse files Browse the repository at this point in the history
  • Loading branch information
cxmeel committed Mar 13, 2024
1 parent 79d4052 commit 5d1ecac
Show file tree
Hide file tree
Showing 42 changed files with 539 additions and 10 deletions.
2 changes: 2 additions & 0 deletions luauspec.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#! use("wally.toml")
name = "cxmeel/sift"
2 changes: 1 addition & 1 deletion src/Array/concat.luau
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
concat(array1, array2) -- {1, 2, 3, 4, 5, 6}
```
]=]
local function concat<T>(...: { T })
local function concat<T>(...: { any }): { T }
local out = {}

for _, array in { ... } do
Expand Down
12 changes: 6 additions & 6 deletions src/Array/count.luau
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local reduce = require("./reduce")

--[=[
@within Array
Expand All @@ -13,15 +15,13 @@
```
]=]
local function count<T>(array: { T }, predicate: ((value: T, index: number) -> boolean)?): number
local counter = 0

for index, value in array do
return reduce(array, function(acc: number, value, index)
if not predicate or predicate(value, index) then
counter += 1
return acc + 1
end
end

return counter
return acc
end, 0)
end

return count
2 changes: 1 addition & 1 deletion src/Array/flatten.luau
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
local INF = newproxy(false)
local INF = newproxy()

--[=[
@within Array
Expand Down
30 changes: 30 additions & 0 deletions src/Dictionary/count.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--[=[
@within Dictionary
Returns the number of elements in the dictionary. If a `predicate` is provided, it will only count the elements that satisfy the predicate.
```lua
local dictionary = { a = 1, b = 2, c = 3 }
count(dictionary) -- 3
count(dictionary, function(value)
return value > 1
end) -- 2
```
]=]
local function count<K, V>(
dictionary: { [K]: V },
predicate: ((value: V, key: K) -> boolean)?
): number
local counter = 0

for key, value in dictionary do
if not predicate or predicate(value, key) then
counter += 1
end
end

return counter
end

return count
Empty file removed src/Dictionary/count.luau.todo
Empty file.
21 changes: 21 additions & 0 deletions src/Dictionary/entries.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--[=[
@within Dictionary
Returns an array of key-value pairs from a dictionary.
```lua
entries({ a = 1, b = 2, c = 3 })
-- { { "a", 1 }, { "b", 2 }, { "c", 3 } }
```
]=]
local function entries<K, V>(dictionary: { [K]: V }): { { K | V } }
local out = {}

for key, value in dictionary do
table.insert(out, { key, value :: any })
end

return out
end

return entries
Empty file removed src/Dictionary/entries.luau.todo
Empty file.
22 changes: 22 additions & 0 deletions src/Dictionary/every.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--[=[
@within Dictionary
Returns `true` if all values in the dictionary pass the test implemented by the provided function.
```lua
every({ a = 1, b = 3, c = 5 }, function(value)
return value % 2 == 0
}) -- false
```
]=]
local function every<K, V>(dictionary: { [K]: V }, predicate: (value: V, key: K) -> boolean): boolean
for key, value in dictionary do
if not predicate(value, key) then
return false
end
end

return true
end

return every
Empty file removed src/Dictionary/every.luau.todo
Empty file.
45 changes: 45 additions & 0 deletions src/Dictionary/expandKeys.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
type ExpandedDictionary<V> = { [string]: ExpandedDictionary<V> | V }

--[=[
@within Dictionary
Expands a dictionary with keys that contain a separator into a nested dictionary.
```lua
local dictionary = {
["a"] = 1,
["b.c"] = 2,
["b.d"] = 3,
}
expandKeys(dictionary) -- { a = 1, b = { c = 2, d = 3 } }
```
]=]
local function expandKeys<V>(
dictionary: { [string]: V },
separator: string?
): ExpandedDictionary<V>
local withSeparator = separator or "."
local out = {}

for key, value in dictionary do
local parts = key:split(withSeparator)
local current = out

while #parts > 1 do
local part = table.remove(parts, 1)

if not current[part] then
current[part] = {}
end

current = current[part]
end

current[parts[1]] = value :: any
end

return out :: any
end

return expandKeys
27 changes: 27 additions & 0 deletions src/Dictionary/filter.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--[=[
@within Dictionary
Filters out elements from a dictionary based on a filter function. The filter function should return `true` to keep the element, and `false` to remove it.
```lua
filter({ a = 1, b = 2, c = 3 }, function(value)
return value > 1
end) -- { b = 2, c = 3 }
```
]=]
local function filter<K, V>(
dictionary: { [K]: V },
filterer: (value: V, key: K) -> boolean
): { [K]: V }
local out = {}

for key, value in dictionary do
if filterer(value, key) then
out[key] = value
end
end

return out
end

return filter
Empty file removed src/Dictionary/filter.luau.todo
Empty file.
30 changes: 30 additions & 0 deletions src/Dictionary/flatten.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
local INF = newproxy()

--[=[
@within Dictionary
Flattens nested dictionaries into a single dictionary. The `depth` parameter specifies the depth of the flattening. If `depth` is not specified, the dictionary is flattened as much as possible.
]=]
local function flatten<K, V>(dictionary: { [any]: any }, depth: number?): { [K]: V }
local withDepth = depth or INF
local out = {}

for key, value in dictionary do
if typeof(value) == "table" and (withDepth == INF or withDepth > 0) then
local useDepth = withDepth == INF and INF or withDepth - 1
local nested = flatten(value, useDepth)

for nestedKey, nestedValue in nested do
out[nestedKey] = nestedValue
end

continue
end

out[key] = value
end

return out
end

return flatten
Empty file removed src/Dictionary/flatten.luau.todo
Empty file.
36 changes: 36 additions & 0 deletions src/Dictionary/flattenKeys.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
local INF = newproxy()

type FlattenableDictionary<V> = { [string]: FlattenableDictionary<V> | V }

--[=[
@within Dictionary
]=]
local function flattenKeys<V>(
dictionary: FlattenableDictionary<any>,
depth: number?,
separator: string?
): { [string]: V }
local withSeparator = separator or "."
local withDepth = depth or INF

local out: { [string]: V } = {}

for key, value in dictionary do
if typeof(value) == "table" and depth > 0 then
local useDepth = withDepth == INF and INF or withDepth - 1
local nested = flattenKeys(value, useDepth, withSeparator)

for nestedKey, nestedValue in nested do
out[`{key}{withSeparator}{nestedKey}`] = nestedValue
end

continue
end

out[key] = value :: V
end

return out
end

return flattenKeys
34 changes: 34 additions & 0 deletions src/Dictionary/freezeDeep.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--[=[
@within Dictionary
Recursively freezes a dictionary and all of its nested dictionaries. Using [Dictionary.freeze] will only freeze the top level of the dictionary, leaving nested dictionaries mutable.
```lua
local frozen = freezeDeep({
a = "apple",
b = { c = "carrot" },
})
frozen.a = "avocado" -- error!
frozen.b.c = "cabbage" -- error!
frozen.c = "coconut" -- error!
```
]=]
local function freezeDeep<K, V>(dictionary: { [K]: V }): { [K]: V }
local out = {}

for key, value in dictionary do
if typeof(value) == "table" then
local nested: any = freezeDeep(value)
out[key] = nested

continue
end

out[key] = value
end

return table.freeze(out)
end

return freezeDeep
Empty file.
23 changes: 23 additions & 0 deletions src/Dictionary/fromArrays.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--[=[
@within Dictionary
Constructs a dictionary from two arrays, one containing the keys and the other containing the values.
```lua
local keys = { "a", "b", "c" }
local values = { 1, 2, 3 }
fromArrays(keys, values) -- { a = 1, b = 2, c = 3 }
```
]=]
local function fromArrays<K, V>(keys: { K }, values: { V }): { [K]: V }
local out = {}

for index, key in keys do
out[key] = values[index]
end

return out
end

return fromArrays
Empty file.
21 changes: 21 additions & 0 deletions src/Dictionary/fromEntries.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--[=[
@within Dictionary
Constructs a dictionary from an array of key-value pairs.
```lua
fromEntries({ { "a", 1 }, { "b", 2 }, { "c", 3 } })
-- { a = 1, b = 2, c = 3 }
```
]=]
local function fromEntries<K, V>(entries: { { K | V } }): { [K]: V }
local out = {}

for _, entry in entries do
out[entry[1] :: K] = entry[2] :: V
end

return out
end

return fromEntries
Empty file.
15 changes: 15 additions & 0 deletions src/Dictionary/get.luau
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
--[=[
@within Dictionary
Retrieves a value from a dictionary using a key. If the key is a dot-separated string and the `dotSeparated` parameter is `true`, the function will traverse the dictionary using the parts of the string as keys. If the key is not found, the function will return `nil`.
```lua
local dictionary = {
foo = {
bar = {
baz = "qux"
}
}
}
get(dictionary, "foo.bar.baz") -- nil
get(dictionary, "foo.bar.baz", true) -- "qux"
```
]=]
local function get<K, V>(dictionary: { [K]: V }, key: K, dotSeparated: boolean?): V?
if not dotSeparated then
Expand Down
34 changes: 34 additions & 0 deletions src/Dictionary/map.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
local None = require("@Sift/None")

--[=[
@within Dictionary
Processes each value and key of a dictionary with a mapper function and returns a new dictionary with the results. If no key is returned by the mapper, the original key is used.
```lua
map({ hello = "world" }, function(value, key)
return `{value}!`, key:upper()
end) -- { HELLO = "world!" }
map({ hello = "world" }, function(value)
return value:upper()
end) -- { hello = "WORLD" }
```
]=]
local function map<K, V, X, Y>(dictionary: { [K]: V }, mapper: (value: V, key: K) -> (Y, X)): { [X]: Y }
local out: { [X]: Y } = {}

for key, value in dictionary do
local mappedValue, mappedKey = mapper(value, key)

if mappedKey == None or mappedValue == None then
continue
end

out[mappedKey or key :: any] = mappedValue
end

return out
end

return map
Empty file removed src/Dictionary/map.luau.todo
Empty file.
Loading

0 comments on commit 5d1ecac

Please sign in to comment.