I have two node object, like this:
school:
grade:
class:
name: bob
school:
grade:
class:
age: 18
I want to merge it, the result like this:
school:
grade:
class:
name: bob
age: 18
How to merge it? when the node size and depth do not kown.
Here is my attempt:
#include <yaml-cpp/yaml.h>
inline const YAML::Node & cnode(const YAML::Node &n) {
return n;
}
YAML::Node merge_nodes(YAML::Node a, YAML::Node b)
{
if (!b.IsMap()) {
// If b is not a map, merge result is b, unless b is null
return b.IsNull() ? a : b;
}
if (!a.IsMap()) {
// If a is not a map, merge result is b
return b;
}
if (!b.size()) {
// If a is a map, and b is an empty map, return a
return a;
}
// Create a new map 'c' with the same mappings as a, merged with b
auto c = YAML::Node(YAML::NodeType::Map);
for (auto n : a) {
if (n.first.IsScalar()) {
const std::string & key = n.first.Scalar();
auto t = YAML::Node(cnode(b)[key]);
if (t) {
c[n.first] = merge_nodes(n.second, t);
continue;
}
}
c[n.first] = n.second;
}
// Add the mappings from 'b' not already in 'c'
for (auto n : b) {
if (!n.first.IsScalar() || !cnode(c)[n.first.Scalar()]) {
c[n.first] = n.second;
}
}
return c;
}
For non-scalar keys I have opted to ignore node equivalence. Please note that this version does not modify a. It returns a new map c which is a merge of b into a. Values from b will replace identically keyed non-map values from a in the c map.
I found an issue with md5i's answer that it doesn't merge the second node's unique sub nodes. My fix was calling the function again in the for loop of node b (which I renamed the override node). I also made everything const, because I'm not editing anything here, and so I don't have to cast it. I'm also reinforcing the fact the second node is overriding the other.
const YAML::Node mergeNodes(const YAML::Node& defaultNode, const YAML::Node& overrideNode)
{
if (!overrideNode.IsMap()) {
// If overrideNode is not a map, merge result is overrideNode, unless overrideNode is null
return overrideNode.IsNull() ? defaultNode : overrideNode;
}
if (!defaultNode.IsMap()) {
// If defaultNode is not a map, merge result is overrideNode
return overrideNode;
}
if (!defaultNode.size()) {
return YAML::Node(overrideNode);
}
// Create a new map 'newNode' with the same mappings as defaultNode, merged with overrideNode
auto newNode = YAML::Node(YAML::NodeType::Map);
for (auto node : defaultNode) {
if (node.first.IsScalar()) {
const std::string& key = node.first.Scalar();
if (overrideNode[key]) {
newNode[node.first] = mergeNodes(node.second, overrideNode[key]);
continue;
}
}
newNode[n.first] = node.second;
}
// Add the mappings from 'overrideNode' not already in 'newNode'
for (auto node : overrideNode) {
if (!node.first.IsScalar()) {
const std::string& key = node.first.Scalar();
if (defaultNode[key]) {
newNode[node.first] = mergeNodes(defaultNode[key], node.second);
continue;
}
}
newNode[node.first] = node.second;
}
return YAML::Node(newNode);
}
This will probably not do the N thingy any favours, but it will take the nodes from node b/overrideNode even if they have no instance in node a/defaultNode.
Related
I am new to Motoko and internet computer, when I am working I am having too much difficulties that might look simple, I am having difficulties doing this, posting the link to forum question here
https://forum.dfinity.org/t/how-to-traverse-inner-array-of-a-trie/12941?u=manubodhi
Please help if somebody is well versed in Motoko and Dfinity
I prepared a small code in motoko playground for you in order to see how you can traverse inner array and achieve your goal of filtering Trie. Here is as well saved project in motoko playground: https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/?tag=1150943578
Shortly to filter through inner array you can use:
let trieOfDishes = Trie.filter<DishId, Dish>(dishes, func (k, v) {
Array.find<MealTypeId>(v.mealTypeId, func(x : MealTypeId) { x == mealTypeId }) != null ;
});
Full code of canister implementation:
import Trie "mo:base/Trie";
import Array "mo:base/Array";
import Iter "mo:base/Iter";
import Nat32 "mo:base/Nat32";
actor Dishes {
type DishId = Nat32;
type DishTypeId = Nat32;
type MealTypeId = Nat32;
public type Dish = {
dishId: DishId;
dishTypeId : DishTypeId;
mealTypeId : [MealTypeId]
};
var dishes: Trie.Trie<DishId, Dish> = Trie.empty();
private func key(x : DishId) : Trie.Key<DishId> {
return { hash = x; key = x };
};
public func add_dish(dish: Dish) : async Dish {
dishes := Trie.replace(dishes, key(dish.dishId), Nat32.equal, ?dish).0;
return dish;
};
public query func getDishesByDishId (dishTypeId : DishTypeId) : async [(DishId, Dish)] {
let trieOfDishes = Trie.filter<DishId, Dish>(dishes, func (k, v) { v.dishId == dishTypeId } );
let arrayOfDishes : [(DishId, Dish)] = Iter.toArray(Trie.iter(trieOfDishes));
return arrayOfDishes;
};
public query func getDishesBymealTypeId (mealTypeId : MealTypeId) : async [(DishId, Dish)] {
let trieOfDishes = Trie.filter<DishId, Dish>(dishes, func (k, v) {
Array.find<MealTypeId>(v.mealTypeId, func(x : MealTypeId) { x == mealTypeId }) != null ;
});
let arrayOfDishes : [(DishId, Dish)] = Iter.toArray(Trie.iter(trieOfDishes));
return arrayOfDishes;
};
}
Noob question:
I want to mutate a value that exists in an array list. I initially tried to just grab the indexed item and directly change its field value.
const Foo = struct {
const Self = #This();
foo: u8,
};
pub fn main() anyerror!void {
const foo = Foo {
.foo = 1,
};
const allocator = std.heap.page_allocator;
var arr = ArrayList(Foo).init(allocator);
arr.append(foo) catch unreachable;
var a = arr.items[0];
std.debug.warn("a: {}", .{a});
a.foo = 2;
std.debug.warn("a: {}", .{a});
std.debug.warn("arr.items[0]: {}", .{arr.items[0]});
//In order to update the memory in [0] I have to reassign it to a.
//arr.items[0] = a;
}
However, the result is unexpected to me:
a: Foo{ .foo = 1 }
a: Foo{ .foo = 2 }
arr.items[0]: Foo{ .foo = 1 }
I would have thought that arr.items[0] would now equal Foo{ .foo = 2 }.
This is probably because I misunderstand slices.
Does a not point to the same memory as arr.items[0]?
Does arr.items[0] return a pointer to a copied item?
var a = arr.items[0];
That is making a copy of the item in arr.items[0].
If you want a reference, write var a = &arr.items[0]; instead.
I'm trying to use the BSPMapGen demo to generate maps for my game but I'm having issues. The resulting map contains too many rooms and the hallways are twice as thick as they need to be. My guess was that the code uses the game dimensions somewhere but I couldn't find it.
screenshot
Here is the code (only has several changes from GenerateState):
package mapgen;
import flixel.tile.FlxTilemap;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flixel.util.FlxStringUtil;
import flixel.FlxG;
import flixel.util.FlxColor;
class BSPMap extends FlxTilemap
{
var TILE_SIZE:Int = 16;
public static var mapData:BitmapData;
var rooms:Array<Rectangle>;
var hallways:Array<Rectangle>;
var leafs:Array<Leaf>;
var mapWidth:Int;
var mapHeight:Int;
public function new(w:Int, h:Int, tilesize:Int)
{
super();
TILE_SIZE = tilesize;
mapWidth = w;
mapHeight = h;
generateMap();
var csvData:String = FlxStringUtil.bitmapToCSV(mapData);
this.loadMapFromCSV(csvData, "assets/tiles.png", TILE_SIZE, TILE_SIZE, AUTO);
}
function generateMap():Void
{
// Reset mapData
mapData = new BitmapData(mapWidth, mapHeight, false, FlxColor.BLACK);
// Reset arrays
rooms = [];
hallways = [];
leafs = [];
// First, create leaf to be root of all leaves
var root = new Leaf(0, 0, mapWidth, mapHeight);
leafs.push(root);
var didSplit:Bool = true;
// Loop through every Leaf in array until no more can be split
while (didSplit)
{
didSplit = false;
for (leaf in leafs)
{
if (leaf.leftChild == null && leaf.rightChild == null) // If not split
{
// If this leaf is too big, or 75% chance
if (leaf.width > Leaf.MAX_SIZE || leaf.height > Leaf.MAX_SIZE || FlxG.random.float() > 0.25)
{
if (leaf.split()) // split the leaf!
{
// If split worked, push child leafs into vector
leafs.push(leaf.leftChild);
leafs.push(leaf.rightChild);
didSplit = true;
}
}
}
}
}
// Next, iterate through each leaf and create room in each one
root.createRooms();
for (leaf in leafs)
{
// Then draw room and hallway (if there is one)
if (leaf.room != null)
{
drawRoom(leaf.room);
}
if (leaf.hallways != null && leaf.hallways.length > 0)
{
drawHalls(leaf.hallways);
}
}
//updateSprite();
}
function drawHalls(hallRect:Array<Rectangle>):Void
{
for (rect in hallRect)
{
hallways.push(rect);
mapData.fillRect(rect, FlxColor.WHITE);
}
}
/**
* Add this room to room array, then draw onto mapData
*/
function drawRoom(roomRect:Rectangle):Void
{
rooms.push(roomRect);
mapData.fillRect(roomRect, FlxColor.WHITE);
}
}
The program aims to use a loop to check if the index of a iterator variable meets certain criteria (i.g., index == 3). If find the desired index, return Some(123), else return None.
fn main() {
fn foo() -> Option<i32> {
let mut x = 5;
let mut done = false;
while !done {
x += x - 3;
if x % 5 == 0 {
done = true;
}
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
return None; //capture all other other possibility. So the while loop would surely return either a Some or a None
}
}
}
The compiler gives this error:
error[E0308]: mismatched types
--> <anon>:7:9
|
7 | while !done {
| ^ expected enum `std::option::Option`, found ()
|
= note: expected type `std::option::Option<i32>`
= note: found type `()`
I think the error source might be that a while loop evaluates to a (), thus it would return a () instead of Some(123). I don't know how to return a valid Some type inside a loop.
The value of any while true { ... } expression is always (). So the compiler expects your foo to return an Option<i32> but finds the last value in your foo body is ().
To fix this, you can add a return None outside the original while loop. You can also use the loop construct like this:
fn main() {
// run the code
foo();
fn foo() -> Option<i32> {
let mut x = 5;
loop {
x += x - 3;
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
if x % 5 == 0 {
return None;
}
}
}
}
The behaviour of while true { ... } statements is maybe a bit quirky and there have been a few requests to change it.
Is any way, how to create two classes, which will be referenced both way and will use only one FK? This interest me in One-to-One as like as One-to-Many cases.
f.e.:
Class First: Entity
{
Second second;
}
Class Second: Entity
{
First first;
}
String TwoWayReference()
{
First Fir = new First();
Second Sec = new Second();
Fir.second = Sec; // I need it is equivalent to: Sec.first = Fir;
if (Sec.first == Fir)
return "Is any way how to do this code works and code return this string?";
else
return "Or it is impossible?"
}
simplest would be
class First : Entity
{
private Second second;
public virtual Second Second
{
get { return this.second; }
set {
if (value != null)
{
value.First = this;
this.second = value;
}
}
}
}
class Second : Entity
{
private First first;
public virtual First First
{
get { return this.first; }
set {
if (value != null && value.Second != this)
{
value.Second = this;
this.first = value;
}
}
}
}