-
-
Notifications
You must be signed in to change notification settings - Fork 537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preserving ordering in sol2 tables and converting to ordered JSON #1609
Comments
Sol tables are just lua tables. And lua tables are not guaranteed to be ordered, so the assumption in step 1 that the code creates the table with specific order is incorrect. As I understand, tables are basically hash maps.
The for_each documentation tells that it does not guarantee order.
Furthermore, in lua you can only A) iterate through a table by specifying the keys to iterate yourself and accessing that specific key in a loop, B) by iterating from 1 up to the first absent index (ipairs), C) by looping through all keys in the table with pairs which does not guarantee order (pairs calls next). for_each in sol is the same as pairs so it will never have guaranteed order even if the data would be ordered, and from the documentation of
To collect all of this together, what you can do is create an ordered list containing the data instead and iterate through it using ipairs (or in sol, just get length and iterate from 1 to length): // myTable = {
// { first = 1 },
// { second = 2 },
// { third = 3 },
// }
sol::table myTable = lua.create_table();
myTable[1] = lua.create_table_with(1, "first", 2, 1);
myTable[2] = lua.create_table_with(1, "second", 2, 2);
myTable[3] = lua.create_table_with(1, "third", 2, 3);
for (int i = 1, count = myTable.size(); i <= count; ++i) {
lua["print"](i, myTable[i], myTable[i][1], myTable[i][2]);
} This is a lot less efficient and inconvenient overall but it guarantees order. There are of course other options, like creating your own data structure, or exposing some kind of C++ data structure to lua depending on what you need. On that vein, if you want an easy way for ordering while having a regular map, you could track the insertions and deletions from the table in a separate list. However, seems that for_each in sol does not check the metamethod -- Define the table with metatable
function createTrackedTable()
local data = {}
local order = {}
local mt = {
__newindex = function(t, k, v)
if v == nil then
-- Remove key from data and order if the value is nil
rawset(data, k, nil)
for i, key in ipairs(order) do
if key == k then
table.remove(order, i)
break
end
end
else
-- Add new key-value pair to data and key to order
if rawget(data, k) == nil then
table.insert(order, k)
end
rawset(data, k, v)
end
end,
__index = data,
__pairs = function(t)
local i = 0
local function iter(data, oldk)
i = i + 1
local k = order[i]
if k then
return k, data[k]
end
end
return iter, data, nil
end,
order = order,
}
return setmetatable({}, mt)
end
-- Usage
local trackedTable = createTrackedTable()
-- Insert elements
trackedTable["first"] = 1
trackedTable["second"] = 2
trackedTable["third"] = 3
-- Print elements in their insertion order
for k, v in pairs(trackedTable) do
print(k, v)
end
-- Remove element
trackedTable["second"] = nil
-- add it back in
trackedTable["second"] = 2
-- Print elements in their insertion order
for k, v in pairs(trackedTable) do
print(k, v)
end // Create a tracked table
sol::function createTrackedTable = lua["createTrackedTable"];
sol::table trackedTable = createTrackedTable();
// Insert elements into the tracked table
trackedTable["first"] = 1;
trackedTable["second"] = 2;
trackedTable["third"] = 3;
// Access the order table
sol::table mt = lua["getmetatable"](trackedTable);
sol::table order = mt["order"];
// Iterate over the order table to print elements
for (int i = 1, count = order.size(); i <= count; ++i) {
sol::object key = order[i];
lua["print"](key, trackedTable[key]);
} In general only lists, stacks, queues and similar data structures have insertion order preserved. Maps and sets order the data on its own, based on the keys or values usually, so they may not suit your needs if you require insertion order being preserved. I would say that JavaScript is quite unique in its implementation of a map style structure with insertion order when iterated (ref). Not sure I have seen anything similar elsewhere yet. 👀 |
@Rochet2 thank you very much for the technical explanation and support. I have continued to read the documentation of both Lua and Sol2 and will try to implement one of your proposed methods to achieve separate sorting. Basically, even if I do not respect the insertion order I need the keys to always be in a certain order before conversion to Json. |
First of all, thank you for this library and the effort that was put into it!
Description
I'm using the sol2 library along with the nlohmann JSON library in my project. I need to preserve the ordering of data in sol tables and then convert them to ordered JSON using the nlohmann JSON library. However, it seems that the default behavior of sol2 doesn't preserve ordering when converting sol tables to JSON.
Expected Behavior:
When converting sol tables to JSON, the ordering of elements should be preserved, ensuring that the JSON output maintains the same order as the original sol table.
Steps to Reproduce:
Output:
Additional Information:
I'm currently using the nlohmann ordered JSON library (nlohmann::ordered_json) to handle JSON objects. It would be helpful to have guidance on how to correctly store data in sol tables while preserving ordering and then convert them to ordered JSON using sol2.
Environment:
Operating System: WSL2 (Debian)
Compiler: Clang 14.0.6 x86_64
sol2 Version: 3.3.0
nlohmann JSON Version: 3.11.2
Full Code:
Thanks a lot for the support!
The text was updated successfully, but these errors were encountered: