Dart: Magic Constant Equivalents - debugging

PHP offers useful magic constants like:
__CLASS__
__FILE__
__METHOD__
and so on. Also the
get_class()
function provides a similar functionality.
Is there anything similar in Dart?

Compiler constants similar to PHP not available. But you can do this manually (not constant value).
This slower but it works.
import 'package:stack_trace/stack_trace.dart';
void main() {
print(__LINE__);
print(__METHOD__);
print(__FILE__);
new Foo();
}
class Foo {
Foo() {
print(__CLASS__);
}
}
String get __CLASS__ {
var frames = new Trace.current().frames;
if(frames.length > 1) {
var member = frames[1].member;
var parts = member.split(".");
if(parts.length > 1) {
return parts[1];
}
}
return null;
}
String get __METHOD__ {
var frames = new Trace.current().frames;
if(frames.length > 1) {
return frames[1].member;
}
return null;
}
String get __FILE__ {
var frames = new Trace.current().frames;
if(frames.length > 1) {
return frames[1].uri.path;
}
return null;
}
int get __LINE__ {
var frames = new Trace.current().frames;
if(frames.length > 1) {
return frames[1].line;
}
return null;
}
4
main
/home/andrew/dart/for_web/test/bin/test.dart
Foo

Related

Spring Boot send only changed data

I am building a game in Spring Boot on a server and classic Javascript on a backend.
Right now I have this:
...
#Autowired
private SimpMessagingTemplate template;
...
#Scheduled(fixedRate = 1000 / Constants.FPS)
public void renderClients() {
for(Game g : games) {
template.convertAndSend("/game/render/" + g.getId(), g);
}
}
...
Basically I have a multiple Games running and I send each with it's id to the client.
However the data I am sending (or the most of the data) is static (not changing)...
What if I want not to send the whole data but only parts which have changed.
Btw the response JSON looks like this:
{"id":"862b1dd8-48d5-4562-802a-7d669a5a5ed5","players":[{"id":"da8dcbec-7028-4a39-9547-a4e2dc321c3c","name":"John Doe","position":{"x":100.0,"y":100.0},"rotation":0.0,"hero":{"maxHealth":1300.0,"movementSpeed":4.5,"attackDamage":32.75,"width":68,"height":71,"heroName":"drowRanger","radius":34.0},"stats":{"kills":0,"lastHits":0},"lastClick":null}],"duration":380107.12}
and the only thing that is changing is duration and sometimes the x and y when the player moves...
Is it even possible?
Could I write some middleware that will do that at the time the objects are converted to JSON?
Maintain a data structure stores your changed value, and attach it to your Game Object.
When the time to send ,convert the map to a json ,and clear it.
Using this way may use more memory than before , but won't cost much time.
I DID IT!!
In my GameController I do:
#Scheduled(fixedRate = 1000 / Constants.FPS)
public void renderClients() throws Exception {
for(Game g : games) {
template.convertAndSend("/game/render/" + g.getId(), g.formatToSend());
}
}
Notice the g.formatToSend() method
here is how a Game class looks like:
public class Game {
private BandWidthOptimizer optimizer = new BandWidthOptimizer();
...
...
public String formatToSend() throws Exception {
return optimizer.optimize(this);
}
}
And Here Comes THE BandWidthOptimizer:
package com.iddqd.doto.optimization;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.tools.classfile.Opcode;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import java.util.Iterator;
import java.util.Set;
import java.util.function.BiConsumer;
public class BandWidthOptimizer {
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
private String[] preserveKeys;
public BandWidthOptimizer() {
this.preserveKeys = new String[0];
}
public BandWidthOptimizer(String[] preserveKeys) {
this.preserveKeys = preserveKeys;
}
public String optimize(Object obj) throws Exception {
String json = mapper.writeValueAsString(obj);
Object nobj = parser.parse(json);
Object oobj = parser.parse(lastJSON);
JSONObject newJsonObj = (JSONObject)nobj;
JSONObject oldJsonObj = (JSONObject)oobj;
JSONObject res = getJSONObjectDiff(newJsonObj, oldJsonObj);
lastJSON = json;
return res.toJSONString();
}
private JSONObject getJSONObjectDiff(JSONObject obj1, JSONObject obj2) {
JSONObject res = new JSONObject();
Set set = obj1.keySet();
for (Object key : set) {
// If doesn't exist put it in the diff
if (!obj2.containsKey(key)) {
res.put(key, obj1.get(key));
} else {
// Get the values from both objects
Object val1 = obj1.get(key);
Object val2 = obj2.get(key);
// If their instances are of the same type
if(val1 == null) {
continue;
}
if(val2 == null) {
res.put(key, val1);
continue;
}
if (val1.getClass().equals(val2.getClass())) {
// If they are JSONObject
if (val1 instanceof JSONObject) {
// Recursively parse JSONObject with all of it's properties
JSONObject nested = getJSONObjectDiff((JSONObject) obj1.get(key), (JSONObject) obj2.get(key));
// If it contains any keys
if(nested.keySet().size() > 0) {
// Store the diff into final diff
res.put(key, nested);
}
// If they are JSONArrays
} else if (val1 instanceof JSONArray) {
// If val1 contains some values (is not empty)
if(((JSONArray) val1).size() > 0) {
// Get their diff
JSONArray arr = getJSONArrayDiff((JSONArray) val1, (JSONArray) val2);
// If array is not empty
if (arr.size() > 0) {
// put it into the diff
res.put(key, arr);
}
}
// If they are just a pure values
} else {
// Compare them - If they're not equal
if(!val1.equals(val2)) {
// put the val1 into diff
res.put(key, val1);
}
}
} else {
res.put(key, val1);
}
}
}
return res;
}
private JSONArray getJSONArrayDiff(JSONArray arr1, JSONArray arr2) {
JSONArray res = new JSONArray();
// For every element
for(int i = 0; i < arr1.size(); i++) {
Object val1 = arr1.get(i);
// If i is out of arr2 bounds
if(i > arr2.size()) {
// put the arr1 item into the diff
res.add(val1);
}
Object val2 = arr2.get(i);
if(val1 == null) {
continue;
}
if(val2 == null) {
res.add(val1);
continue;
}
// If their types are equal
if(val1.getClass().equals(val2.getClass())) {
// If they are JSONObjects
if(val1 instanceof JSONObject) {
// Get their diff
JSONObject obj = getJSONObjectDiff((JSONObject) val1, (JSONObject) val2);
// If it contains any keys
if(obj.keySet().size() > 0) {
// Store the diff into final diff
res.add(obj);
}
// If they are JSONArrays
} else if (val1 instanceof JSONArray) {
// Get their diff
JSONArray arr = getJSONArrayDiff((JSONArray) val1, (JSONArray) val2);
// If array is not empty
if(arr.size() > 0) {
// put it into the diff
res.add(arr);
}
// If they are just a pure values
} else {
// Compare them - If they're not equal
if(val1 != val2) {
// add the val1 into diff
res.add(val1);
}
}
} else {
res.add(val1);
}
}
return res;
}
}
This is it, now if nothing moves on the map the result JSON looks like this:
{"duration":282964.56}
because only the duration changes
But when my Player moves on the map see what happens:
{"duration":386676.06,"players":[{"position":{"x":556.5914801003707,"y":153.55964799554002}}]}
TODO
I have to implement a preserveKeys functionallity because I always want to send some keys like id and so on...

Telerik RadPageview Right-to-Left Layout Arrow Keys

I have a RadPageview in Strip Mode and I want my program to had support a right-to-left language. When I changing the Right-to-Left Property to true it works fine. but when I run my program and use Arrow Keys (Left or Right) it doesn't work correctly. Left key goes to Right and Right key goes to Left. How can I fix it?
Telerik answered my question:
http://www.telerik.com/forums/right-to-left-arrow-keys-problem
the possible solution that I can suggest is to create a custom RadPageViewStripElement and override its IsNextKey and IsPreviousKey methods on a way to reverse the previous/next navigation as follows:
public class CustomPageView : RadPageView
{
public override string ThemeClassName
{
get
{
return typeof(RadPageView).FullName;
}
}
protected override RadPageViewElement CreateUI()
{
switch (this.ViewMode)
{
case PageViewMode.Strip:
return new CustomRadPageViewStripElement();
default:
return base.CreateUI();
}
}
}
public class CustomRadPageViewStripElement : RadPageViewStripElement
{
public CustomRadPageViewStripElement()
{
}
protected override Type ThemeEffectiveType
{
get
{
return typeof(RadPageViewStripElement);
}
}
protected override bool IsNextKey(Keys key)
{
if (this.RightToLeft)
{
if (key == Keys.Left)
{
return true;
}
else
{
return false;
}
}
return base.IsNextKey(key);
}
protected override bool IsPreviousKey(Keys key)
{
if (this.RightToLeft)
{
if (key == Keys.Right)
{
return true;
}
else
{
return false;
}
}
return base.IsPreviousKey(key);
}
}
I fix it programmatically by using ProcessCmdKey of form.
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Right))
{
int x = radPageView1.Pages.IndexOf(this.radPageView1.SelectedPage) - 1;
if (x < 0)
x = radPageView1.Pages.Count() - 1;
else if (x >= radPageView1.Pages.Count())
x = 0;
radPageView1.SelectedPage = radPageView1.Pages[x];
return true;
}
else if(keyData == Keys.Left)
{
int x = radPageView1.Pages.IndexOf(this.radPageView1.SelectedPage) + 1;
if (x <= 0)
x = radPageView1.Pages.Count() - 1;
else if (x >= radPageView1.Pages.Count())
x = 0;
radPageView1.SelectedPage = radPageView1.Pages[x];
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}

Dart JavaScript output fails with: method not found: 'new ToDos:1:0' Receiver: Instance of 'JsClassMirror'

I've ported a handy JS library to Dart: dartscale. The crucial part of it's functionality can be broken down to:
final Map<Symbol, ClassMirror> _registeredModules = new Map<Symbol, ClassMirror>();
register(module, [String moduleName]) {
final uniqueModuleName = moduleName != null ? moduleName : module.runtimeType.toString();
final Symbol uniqueModuleIdentifier = new Symbol(uniqueModuleName);
if (_registeredModules.containsKey(uniqueModuleName)) {
throw new StateError("Module ${moduleName} already registered!");
}
final ClassMirror mirror = reflect(module).type;
_registeredModules[uniqueModuleIdentifier] = mirror;
}
start(Symbol moduleName, String id, options) {
if (!_registeredModules.containsKey(moduleName)) {
throw new StateError("Module ${moduleName} not registered!");
}
final ClassMirror mirror = _registeredModules[moduleName];
final Symbol moduleId = id != null ? new Symbol(id) : moduleName;
final Sandbox sandbox = new Sandbox(this.mediator);
if (_runningModules.containsKey(moduleId)) {
throw new StateError("Module with id #${moduleId} already running!");
}
final InstanceMirror moduleInstance = mirror.newInstance(new Symbol(''), [sandbox], null);
moduleInstance.invoke(new Symbol("start"), [options]);
_runningModules[moduleId] = moduleInstance;
}
i also provide an example
part of example;
class ToDos {
Sandbox _sandbox;
DivElement _contentEl;
int _nextToDoId = 0;
UListElement _todoList;
ToDos ([Sandbox this._sandbox]);
start([Map options]) {
this._initLocalStorage();
var html = ['<div id="module-todos">',
'<form>',
'<input type="text" class="input-medium">',
'<button type="submit" class="btn">Add</button>',
'</form>',
'<ul>',
'</ul>',
'</div>'].join('');
this._contentEl = new Element.html(html);
this._todoList = this._contentEl.query('ul');
options['containerEl'].append(this._contentEl);
window.localStorage.keys.forEach((key) => this._renderToDo(key));
this._setupEvents();
this._sandbox.channel('navigationbar').topic('filter').listen((filterContent) {
this._filter(filterContent);
});
this._sandbox.channel('navigationbar').topic('clearfilter').listen((filterContent) {
this._todoList.queryAll('li span').forEach((element) => element.parent.classes.remove('hide'));
});
}
stop() {
this._contentEl.remove();
}
_initLocalStorage() {
if (window.localStorage.keys.length == 0) {
var map = {
"1": {
"subject": "Groceries: Banas and Apples",
"isDone": false
},
"2": {
"subject": "Taxes: take care of them",
"isDone": false
},
"3": {
"subject": "Bring out trash",
"isDone": false
}
};
for (var key in map.keys) {
window.localStorage[key] = stringify(map[key]);
this._nextToDoId++;
}
}
else {
for (var key in window.localStorage.keys) {
var intKey = int.parse(key);
if (intKey > this._nextToDoId) {
this._nextToDoId = intKey;
}
this._nextToDoId++;
}
}
}
_setupEvents() {
var input = this._contentEl.query('input');
input.onKeyDown.listen((event) {
if (event.keyCode == KeyCode.ENTER) {
event.preventDefault();
this._addToDo(input.value);
input.value = '';
}
});
this._contentEl.query('button[type="Submit"]').onClick.listen((event) {
event.preventDefault();
if (input.value.length > 0) {
this._addToDo(input.value);
input.value = '';
}
});
this._todoList.onClick.listen((MouseEvent event) {
var el = event.target;
if (el.classes.contains('icon-remove')) {
this._deleteToDo(el.parent);
}
else if (el.classes.contains('icon-ok')) {
this._toggleToDoDone(el.parent);
}
});
}
_renderToDo(id) {
var todoObject = parse(window.localStorage[id.toString()]);
var html = ['<li class="record-todo ', todoObject["isDone"]?"done":"",'" data-id="', id,'">',
'<span>', todoObject["subject"], '</span>',
'<i class="icon icon-ok"></i>',
'<i class="icon icon-remove"></i>',
'</li>'].join('');
this._todoList.append(new Element.html(html));
}
_addToDo(text) {
var todoJson = stringify({
"subject": text,
"isDone": false
});
window.localStorage[this._nextToDoId.toString()] = todoJson;
this._renderToDo(this._nextToDoId);
this._nextToDoId++;
}
_deleteToDo(todoLIElement) {
window.localStorage.remove(todoLIElement.dataset["id"]);
todoLIElement.remove();
}
_toggleToDoDone(todoLIElement) {
var done = !todoLIElement.classes.contains('done');
var id = todoLIElement.dataset["id"];
var todoObject = parse(window.localStorage[id]);
todoObject["isDone"] = done;
window.localStorage[id] = stringify(todoObject);
if (done) {
todoLIElement.classes.add('done');
}
else {
todoLIElement.classes.remove('done');
}
}
_filter(content) {
this._todoList.queryAll('li span').forEach((element) {
if (element.innerHtml.contains(content)) {
element.parent.classes.remove('hide');
}
else {
element.parent.classes.add('hide');
}
});
}
}
in my App.dart
library example;
import 'dart:html';
import 'dart:json';
import '../lib/dartscale.dart';
part 'dart/ToDos.dart';
main () {
var core = new Core();
core.register(new ToDos());
core.start("ToDos", "ToDos", {
"containerEl": query('body > .container')
});
}
bug in dart2js ?
Solution
Turns out that dart2js has problems with mirrored calls on Methods/Constructors which have optional positional Arguments.
So changing
class ToDos {
Sandbox _sandbox;
ToDos([Sandbox this._sandbox]);
}
to
class ToDos {
Sandbox _sandbox;
ToDos(Sandbox this._sandbox); //make the argument non-optional
}
solved my Problem
This isn't really an answer, since you don't state what's going wrong, but some general advice. Mostly, I'd avoid new Symbol() and mirrors if you can easily avoid them, and in this case you can.
First, you should figure out if you want to register module instances or produce instances on demand, you probably don't want both like you are here. If you register an instance, then can't you just reuse that instance? Does start() need to produce new instances as part of its spec? You turn around and try to make sure that an instance isn't already running anyway.
If you really do need to produce instances, a simple factory function will eliminate the need for mirrors. So instead of:
core.register(new ToDos());
You write:
core.register('ToDos', () => new ToDos());
If you still want to use mirrors, you can clean up the use of new Symbol(). Here's some recommendations:
Don't use Symbols as keys unless you're really getting them from reflective APIs like mirrors and noSuchMethod in the first place. Just use the String name or maybe the runtimeType. In your case you're mixing Symbols and Strings as keys in your _registeredModules map, which is probably causing some bugs, like modules will never appear to be registered. (are you testing in checked mode?)
Don't use new Symbol('name'), use const Symbol('name')
Don't use InstanceMirror.invoke, getField, or setField when you can just call the method directly. In your code you can replace
moduleInstance.invoke(new Symbol("start"), [options]);
with
moduleInstance.reflectee.start(options);
Factories aren't evil. It'd be nice to invoke a constructor from a type instance, but until then registering a factory is pretty lightweight in Dart.
Here's your code with those suggestions:
typedef Object Factory(Sandbox sandbox);
final Map<Symbol, Factory> _registeredModules = new Map<Type, Factory>();
register(Type type, Factory factory) {
if (_registeredModules.containsKey(type)) {
throw new StateError("Module $type already registered!");
}
_registeredModules[type] = factory;
}
start(Type type, options) {
if (!_registeredModules.containsKey(type)) {
throw new StateError("Module $type not registered!");
}
if (_runningModules.containsKey(type)) {
throw new StateError("Module $type already running!");
}
Sandbox sandbox = new Sandbox(this.mediator);
var module = _runningModules[type](sandbox)..start(options);
_runningModules[type] = module;
}

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Hierarchical structure iteration and LINQ

Assume that we have class
public class RMenuItem
{
public List<RMenuItem> ChildrenItems { get; }
public decimal OperationID { get; }
public string Name { get; }
}
as you can see - each menuitem could have children items - as usual in menu.
My task is to iterate through each items of this list and apply some action to it. Classical decision is to write recursive iteration. But I'm interesting if LINQ could make my task easier? For example, I suppose that we can write query that can get flat list of objects, which i can iterate simply with foreach. But my attempts in this way weren't successful yet.
So any help appreciated!
It's possible:
public void PrintAllNames(RMenuItem rootItem)
{
Action<RMenuItem> print = null;
print = m =>
{
Console.WriteLine(m.Name);
m.ChildrenItems.ForEach(print);
};
print(rootItem);
}
Notice how it's necessary to declare print so that print can use itself. This is directly comparable to a recursive method, which I'd rather use:
public void PrintAllNames(RMenuItem rootItem)
{
Console.WriteLine(rootItem.Name);
rootItem.ChildrenItems.ForEach(PrintAllNames);
}
(although for a more complex situation, maybe the functional solution would make the most sense)
I suggest 2 ways of achieving this. You can opt with an utility method to get all the items or you can implement the Visitor Pattern, though it implies changing the RMenuItem class.
Utility method:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
I prefer an imperative way to a recursive because we can use iterator blocks.
Visitor Pattern:
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
Usage:
RMenuItem m1 = new RMenuItem
{
Name = "M1",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M11" },
new RMenuItem {
Name = "M12",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M121" },
new RMenuItem { Name = "M122" }
}
}
}
};
RMenuItem m2 = new RMenuItem
{
Name = "M2",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M21" },
new RMenuItem { Name = "M22" },
new RMenuItem { Name = "M23" }
}
};
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in GetAllMenuItems(menus))
{
Console.WriteLine(menu);
}
// or
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in menus)
{
menu.Accept(new PrintRMenuItemVisitor());
}
You could difine a Flatten method in your class (or as an extension if you prefer) like this
public IEnumerable<RMenuItem> Flatten()
{
foreach (var item in ChildrenItems)
{
yield return item;
}
return ChildrenItems.SelectMany(item => item.Flatten());
}
then doing somthing with each elements will be as simple as
RMenuItem rootItem ;
// do somthing with the root item
foreach (var item in rootItem.Flatten())
{
// do somthing
}
Indeed you can do that using LINQ, SelectMany flats out the list, just some example
menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);
Thanks
Edit:
In response to the comments, I was just giving an example of SelectMany previously. Thanks for pointing out.
menuItemsList.SelectMany(x => x.ChildrenItems.Select(p => p)).Where(c => c.someChildProperty);
OR something like this
menuItemsList.SelectMany(x => x.ChildrenItems).Select(p => p).Where(c => c.someChildProperty);
Edit2
Ahh .. now I understood what you want ..
We can just slightly modify my above query to do what you want
menuItemsList
.SelectMany(x => { //do something with x like printing it
x.ChildrenItems
})
.Select(p => { // do something with p like printing it
p
});
Basically you can do what you want the element inside the {}
Thanks

Resources