Search ArrayList of Structs in zig - zig

I expect this is a question with a very simple answer about how to do this well in zig.
I want to search an ArrayList of some struct to find a record by one of the fields.
In C++ I would consider using std::find_if and a lambda but there doesn't seem to be anything like this in the zig standard library unless I missed something.
Is there a better / more idiomatic way than the simple loop like below?
const std = #import("std");
const Person = struct {
id: i32,
name: []const u8
};
pub fn main() !void {
const allocator = std.heap.page_allocator;
var data = std.ArrayList(Person).init(allocator);
defer data.deinit();
try data.append(.{.id = 1, .name = "John"});
try data.append(.{.id = 2, .name = "Dave"});
try data.append(.{.id = 8, .name = "Bob"});
try data.append(.{.id = 5, .name = "Steve"});
// Find the id of the person with name "Bob"
//
// -- IS THERE A BETTER WAY IN ZIG THAN THIS LOOP BELOW? --
//
var item_index: ?usize = null;
for (data.items) | person, index | {
if (std.mem.eql(u8, person.name, "Bob")) {
item_index = index;
}
}
std.debug.print("Found index is {}\n", .{item_index});
}

There's not that many built-in utilities present in stdlib, indeed. However, for that piece of code, you may declare the found index as a const:
const item_index = for (data.items) |person, index| {
if (std.mem.eql(u8, person.name, "Bob")) break index;
} else null;

Related

Why does my tree creation fail without the use of inline?

I'm trying to create a Trie structure in Zig using Zigs StringHashMap.
I am able to get it to work a bit, but only by using a "inline" for loop which is not really usable as this requires the paths to be known at compile time :-(
Any help/explanation would be much appreciated :-)
The code:
const std = #import("std");
const Allocator = std.mem.Allocator;
const print = std.debug.print;
const expect = std.testing.expect;
const HashMap = struct {
value: u8,
children: std.StringHashMap(*HashMap),
};
fn newHashMap(allocator: Allocator, value: u8) HashMap {
return HashMap{
.value = value,
.children = std.StringHashMap(*HashMap).init(allocator),
};
}
fn showTree(root: *std.StringHashMap(*HashMap), keys:[3][]const u8 ) void {
var hashMap = root;
for (keys) |key| {
print("get key {s}\n", .{key});
var value = hashMap.get(key);
if (value) |node| {
print("we got a value for {s}:{}\n", .{key,node.value});
hashMap = &node.children;
} else {
print("no value for {s}\n", .{key});
break;
}
}
}
test "HashMap" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const gpaAllocator = gpa.allocator();
var arena = std.heap.ArenaAllocator.init(gpaAllocator);
defer {
arena.deinit();
const leaked = gpa.deinit();
if (leaked) expect(false) catch #panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
}
const allocator = arena.allocator();
var root = &std.StringHashMap(*HashMap).init(allocator);
var hashMap = root;
const keys = [_][]const u8{ "a", "b", "c" };
const values: [3]u8 = .{ 1, 2, 3 };
// create tree
inline for (keys) |key, i| {
print("put key {s}:{}\n", .{ key, values[i] });
var newNode = newHashMap(allocator, values[i]);
try hashMap.put(key, &newNode);
showTree(root,keys);
hashMap = &newNode.children;
}
showTree(root,keys);
}
This prints:
Test [1/1] test "HashMap"...
put key a:1
put key b:2
put key c:3
get key a
we got a value for a:1
get key b
we got a value for b:2
get key c
we got a value for c:3
All 1 tests passed.
as expected.
Removing the 'inline' results in:
Test [1/1] test "HashMap"...
put key a:1
put key b:2
put key c:3
get key a
we got a value for a:3
get key b
no value for b
All 1 tests passed.
The answer turned out to be quite obvious (with hindsight ;-)) as mentioned in 1:
var declarations inside functions are stored in the function's stack frame. Once a function returns, any Pointers to variables in the function's stack frame become invalid references, and dereferencing them becomes unchecked Undefined Behavior.
This explains the strange behaviour in a loop without inline.
The pointers just get overwritten resulting in Undefined Behaviour.
By adding 'inline' the loop is unwound and then there is no pointer reuse, hence the correct output.
The correct way of dealing with this is to allocate the struct explicitly and pass around the pointer to the struct as shown in 2.
Once that is sorted it all makes sense.
https://ziglang.org/documentation/master/#Where-are-the-bytes
https://www.reddit.com/r/Zig/comments/s6v8t3/idiomatic_zig_for_initializing_an_allocated/
For reference, the working code without 'inline' below:
const std = #import("std");
const Allocator = std.mem.Allocator;
const print = std.debug.print;
const expect = std.testing.expect;
const HashMap = struct {
value: u8,
children: std.StringHashMap(*HashMap),
};
fn newHashMap(allocator: Allocator, value: u8) !*HashMap {
const node = try allocator.create(HashMap);
node.* = .{
.value = value,
.children = std.StringHashMap(*HashMap).init(allocator),
};
return node;
}
fn showTree(root: *std.StringHashMap(*HashMap), keys:[3][]const u8 ) void {
var hashMap = root;
for (keys) |key| {
print("get key {s}\n", .{key});
var value = hashMap.get(key);
if (value) |node| {
print("we got a value for {s}:{}\n", .{key,node.value});
hashMap = &node.children;
} else {
print("no value for {s}\n", .{key});
break;
}
}
}
test "HashMap" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const gpaAllocator = gpa.allocator();
var arena = std.heap.ArenaAllocator.init(gpaAllocator);
defer {
arena.deinit();
const leaked = gpa.deinit();
if (leaked) expect(false) catch #panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
}
const allocator = arena.allocator();
var root = &std.StringHashMap(*HashMap).init(allocator);
var hashMap = root;
const keys = [_][]const u8{ "a", "b", "c" };
const values: [3]u8 = .{ 1, 2, 3 };
// create tree
for (keys) |key, i| {
print("put key {s}:{}\n", .{ key, values[i] });
var newNode = try newHashMap(allocator, values[i]);
try hashMap.put(key, newNode);
hashMap = &newNode.children;
}
showTree(root,keys);
}

Looping over an integer range in Zig

Is a while-loop like this the idiomatic way to loop over an integer range in Zig?
var i: i32 = 5;
while (i<10): (i+=1) {
std.debug.print("{}\n", .{i});
}
I first tried the python-like
for (5..10) |i| {
// ....
but that doesn't work.
zig has no concept of integer range loops but there's a hack by nektro which create a random []void slice, so you can iterate with for loop
const std = #import("std");
fn range(len: usize) []const void {
return #as([*]void, undefined)[0..len];
}
for (range(10)) |_, i| {
std.debug.print("{d}\n", .{i});
}

How to reverse a Map in dart? Is there any build in function

There is a map. I want to reverse it. is there any inbuilt function for that in dart?
{203: 5, 201: 3, 204: 1}
This the current Map i have.
{204: 1,201: 3:203: 5}
This is the results i want to get.
Thank You!
Does this work? (I'm on mobile and can't test)
import 'dart:collection';
void main() {
final m = {203: 5, 201: 3, 204: 1};
final reverseM = LinkedHashMap.fromEntries(m.entries.toList().reversed);
print(reverseM);
}
If someone still need a solution, I wrote a simple library to deeply reverse Map, Set and List. Usage is simple, because deepReverse() is an extension method, so all you need to do is to import my library and call the method on your map:
import 'package:deep_collection/deep_collection.dart';
void main() {
print({203: 5, 201: 3, 204: 1}.deepReverse());
}
this doesn't handle the situation where values are duplicated, but if that's not an issue:
Map reverseMap(Map map) => {
for (var e in map.entries) e.value: e.key};
reverseMap({'a': 1, 'b': 2})
// {1: 'a', 2: 'b'}
map.lenght should be map.length
thanks for your code
sort(Map map){
Map newmap;
for(int i =0 ; i< map.length; i++){
String x = map.entries.last.key.toString();
int y = map.entries.last.value;
newmap[x] = y;
map.remove(x);
}
return newmap;
}
this is the simplest way to reverse Map by its keys in Dart language:
reverseMap(Map map) {
Map newmap = {};
for (String _key in map.keys.toList().reversed) {
newmap[_key] = map[_key];
}
return newmap;
}

Is there anything like a struct in dart?

In javascript it always bothered me people use objects as vectors like {x: 1, y: 2} instead of using an array [1,2]. Access time for the array is much faster than the object but accessing by index is more confusing especially if you need a large array. I know dart has fixed arrays but is there a way to name the offsets of an array like you would a struct or a tuple/record in another language? Define enum/constants maybe?
I'd want something like
List<int> myVector = new List([x,y]);
myVector.x = 5;
is there an equivalent or idiomatic way to do this?
That sounds like a class.
class MyVector {
int x;
int y;
MyVector(this.x, this.y);
}
There is no simpler and more efficient way to create a name-indexed structure at runtime. For simplicity you could usually use a Map, but it's not as efficient as a real class.
A class should be at least as efficient (time and memory) as a fixed length list, after all it doesn't have to do an index bounds check.
In Dart 3.0, the language will introduce records. At that point, you can use a record with named fields instead of creating a primitive class:
var myVector = (x: 42, y: 37);
print(myVector.x);
A record is unmodifiable, so you won't be able to update the values after it has been created.
For me, i see 2 way to do this. I will sort by best in my point of view
Class based method
Here, the approach is to encapsulate your need, in a dedicated object
Pros:
It's encapsultate
You can propose several way to access variable, depend of the need
You can extend functionality without break everything
I love it :p
Cons
More time spend to create class, etc.
Do you really need what i say in pros ?
Maybe weird for js people
example :
class Vector {
int x;
int y;
static final String X = "x";
static final String Y = "y";
Vector({this.x, this.y});
Vector.fromList(List<int> listOfCoor) {
this.x = listOfCoor[0];
this.y = listOfCoor[1];
}
// Here i use String, but you can use [int] an redefine static final member
int operator[](String coor) {
if (coor == "x") {
return this.x;
} else if (coor == "y") {
return this.y;
} else {
// Need to be change by a more adapt exception :)
throw new Exception("Wrong coor");
}
}
}
void main() {
Vector v = new Vector(x: 5, y: 42);
Vector v2 = new Vector.fromList([12, 24]);
print(v.x); // print 5
print(v["y"]); // print 42
print(v2.x); // print 12
print(v2[Vector.Y]); // print 24
}
Enum based method:
You can also defined a "enum" (actually not really implement but will be in the future version) that will contains "shortcut" to your value
Pros
More simple to implement
Is more like your example ;p
Cons
Less extendable
i think is not very pretty
Not OOP think
example:
class Vector {
static final int x = 0;
static final int y = 1;
}
void main() {
List<int> myVector = new List(2);
myVector[Vector.x] = 5;
myVector[Vector.y] = 42;
}
Make your choice ;p
This is only possible with a class in Dart.
There are some open feature requests at http://dartbug.com
introduce struct (lightweight class)
Give us a way to structure Bytedata
If you have reasonably big data structure, you can use "dart:typed_data" as a model and provide lightweight view for the stored data. This way the overhead should be minimal.
For example, if you need 4X4 matrix of Uint8 values:
import "dart:typed_data";
import "dart:collection";
import "package:range/range.dart";
class Model4X4Uint8 {
final Uint8List _data;
static const int objectLength = 4 * 4;
final Queue<int> _freeSlotIndexes;
Model4X4Uint8(int length): _data = new Uint8List((length) * objectLength),
_freeSlotIndexes = new Queue<int>.from(range(0, length));
int get slotsLeft => _freeSlotIndexes.length;
num operator [](int index) => _data[index];
operator []=(int index, int val) => _data[index] = val;
int reserveSlot() =>
slotsLeft > 0 ? _freeSlotIndexes.removeFirst() : throw ("full");
void delete(int index) => _freeSlotIndexes.addFirst(index);
}
class Matrix4X4Uint8 {
final int offset;
final Model4X4Uint8 model;
const Matrix4X4Uint8(this.model, this.offset);
num operator [](int index) => model[offset + index];
operator []=(int index, int val) => model[offset + index] = val;
void delete() => model.delete(offset);
}
void main() {
final Model4X4Uint8 data = new Model4X4Uint8(100);
final Matrix4X4Uint8 mat = new Matrix4X4Uint8(data, data.reserveSlot())
..[14] = 10
..[12] = 256; //overlow;
print("${mat[0]} ${mat[4]} ${mat[8]} ${mat[12]} \n"
"${mat[1]} ${mat[5]} ${mat[9]} ${mat[13]} \n"
"${mat[2]} ${mat[6]} ${mat[10]} ${mat[14]} \n"
"${mat[3]} ${mat[7]} ${mat[11]} ${mat[15]} \n");
mat.delete();
}
But this is very low level solution and can easily create sneaky bugs with memory management and overflows.
You could also use an extension on List to create aliases to specific indexes.
Although it will be difficult to set up mutually exclusive aliases, in some cases, it may be a simple solution.
import 'package:test/test.dart';
extension Coordinates<V> on List<V> {
V get x => this[0];
V get y => this[1];
V get z => this[2];
}
void main() {
test('access by property', () {
var position = [5, 4, -2];
expect(position.x, 5);
expect(position.y, 4);
expect(position.z, -2);
});
}
The Tuple package https://pub.dev/packages/tuple might be what you are looking for when a class is too heavy.
import 'package:tuple/tuple.dart';
const point = Tuple2<int, int>(1, 2);
print(point.item1); // print 1
print(point.item2); // print 2

What grammar is this?

I have to parse a document containing groups of variable-value-pairs which is serialized to a string e.g. like this:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Here are the different elements:
Group IDs:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Length of string representation of each group:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
One of the groups:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14 ^VAR1^6^VALUE1^^
Variables:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Length of string representation of the values:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
The values themselves:
4^26^VAR1^6^VALUE1^VAR2^4^VAL2^^1^14^VAR1^6^VALUE1^^
Variables consist only of alphanumeric characters.
No assumption is made about the values, i.e. they may contain any character, including ^.
Is there a name for this kind of grammar? Is there a parsing library that can handle this mess?
So far I am using my own parser, but due to the fact that I need to detect and handle corrupt serializations the code looks rather messy, thus my question for a parser library that could lift the burden.
The simplest way to approach it is to note that there are two nested levels that work the same way. The pattern is extremely simple:
id^length^content^
At the outer level, this produces a set of groups. Within each group, the content follows exactly the same pattern, only here the id is the variable name, and the content is the variable value.
So you only need to write that logic once and you can use it to parse both levels. Just write a function that breaks a string up into a list of id/content pairs. Call it once to get the groups, and then loop through them calling it again for each content to get the variables in that group.
Breaking it down into these steps, first we need a way to get "tokens" from the string. This function returns an object with three methods, to find out if we're at "end of file", and to grab the next delimited or counted substring:
var tokens = function(str) {
var pos = 0;
return {
eof: function() {
return pos == str.length;
},
delimited: function(d) {
var end = str.indexOf(d, pos);
if (end == -1) {
throw new Error('Expected delimiter');
}
var result = str.substr(pos, end - pos);
pos = end + d.length;
return result;
},
counted: function(c) {
var result = str.substr(pos, c);
pos += c;
return result;
}
};
};
Now we can conveniently write the reusable parse function:
var parse = function(str) {
var parts = {};
var t = tokens(str);
while (!t.eof()) {
var id = t.delimited('^');
var len = t.delimited('^');
var content = t.counted(parseInt(len, 10));
var end = t.counted(1);
if (end !== '^') {
throw new Error('Expected ^ after counted string, instead found: ' + end);
}
parts[id] = content;
}
return parts;
};
It builds an object where the keys are the IDs (or variable names). I'm asuming as they have names that the order isn't significant.
Then we can use that at both levels to create the function to do the whole job:
var parseGroups = function(str) {
var groups = parse(str);
Object.keys(groups).forEach(function(id) {
groups[id] = parse(groups[id]);
});
return groups;
}
For your example, it produces this object:
{
'1': {
VAR1: 'VALUE1'
},
'4': {
VAR1: 'VALUE1',
VAR2: 'VAL2'
}
}
I don't think it's a trivial task to create a grammar for this. But on the other hand, a simple straight forward approach is not that hard. You know the corresponding string length for every critical string. So you just chop your string according to those lengths apart..
where do you see problems?

Resources