I have the below class, how do I print the list of objects in format [1,[2,3,8],[[]],10,[]] in C#?
public class InnerList
{
private int val;
private boolean isValue;
private List<InnerList> intList;
}
public string ConvertToString()
{
if (this.isValue)
{
return this.val + "";
}
else
{
return this.intList.ToString();
}
}
In my caller, I will use something like below to print the list of objects in format [1,[2,3,8],[[]],10,[]]
System.out.println(list);
My question is how to achieve this in c#?
Solution:
public class InnerList
{
//public only for simple initialization at usage example
public int val;
public bool isValue;
public List<InnerList> intList;
public override string ToString()
{
if (isValue)
return val.ToString();
return String.Format("[{0}]", intList == null ? "" : String.Join(", ", intList.Select(x => x.ToString())));
}
}
Usage:
var test = new InnerList
{
intList = new List<InnerList> {
new InnerList { isValue = true, val = 1 },
new InnerList { isValue = true, val = 2 },
new InnerList
{
intList = new List<InnerList> {
new InnerList { isValue = true, val = 13 },
new InnerList { isValue = true, val = 23 },
new InnerList()
}
}
}
};
Console.WriteLine(test);//[1, 2, [13, 23, []]]
Welcome to C# world!
I can't understand what you trying to do, but i can show you how we do in by C#:
public class InnerList
{
public int Value
{
get
{
return this.intList.Count;
}
}
public bool HasValue { get; set; }
private List<InnerList> intList;
public static implicit operator string(InnerList list)
{
return list.ToString();
}
public override string ToString()
{
if (this.HasValue)
{
return this.Value.ToString();
}
else
{
return this.intList.ToString();
}
}
}
It looks like you might run into the problem of circular them references here. Because an InnerList could reference its own parent in its intList. That is why I would recommend a serializer to do the job for you. It knows how to handle these circular references. I'm using Newtonsoft.Json here.
public override string ToString()
{
var settings = new JsonSerializerSettings();
settings.PreserveReferencesHandling = PreserveReferencesHandling.All;
return JsonConvert.SerializeObject(this, settings);
}
Related
i'd like to know if there's an opinion how to combine 2 cases of my switch, which are almost the same, but one is for nullable value and the second is not.
switch (rangeA)
{
case Range<int> intRangeA:
{
if (rangeB is Range<int> intRangeB)
{
return intRangeA.ValueFrom <= intRangeB.ValueTo && intRangeA.ValueTo >= intRangeB.ValueFrom;
}
return false;
}
case Range<int?> intRangeA:
{
if (rangeB is Range<int?> intRangeB)
{
return intRangeA.ValueFrom <= intRangeB.ValueTo && intRangeA.ValueTo >= intRangeB.ValueFrom;
}
return false;
}
}
It rather depends on what the data type of rangeA and rangeB are.
Assuming they're object, you can do something like this. It will throw an exception at runtime if you create a Range<Something non-comparable> then call ContainsInclusive on it. You can add an extra check for that if you want, but it gets a bit messy as Nullable<T> doesn't implement any interfaces, so you'll have to resort to reflection.
public class Program
{
public static void Main()
{
Foo(new Range<int>() { ValueFrom = 1, ValueTo = 10 }, new Range<int>() { ValueFrom = 0, ValueTo = 10 });
Foo(new Range<int?>() { ValueFrom = 1, ValueTo = 10 }, new Range<int?>() { ValueFrom = 0, ValueTo = 10 });
}
private static bool Foo(object rangeA, object rangeB)
{
return (rangeA, rangeB) switch
{
(Range<int> a, Range<int> b) => b.ContainsInclusive(a),
(Range<int?> a, Range<int?> b) => b.ContainsInclusive(a),
_ => false,
};
}
}
public class Range<T>
{
public T ValueFrom { get; set; }
public T ValueTo { get; set; }
public bool ContainsInclusive(Range<T> other)
{
return Comparer<T>.Default.Compare(other.ValueFrom, this.ValueTo) <= 0 &&
Comparer<T>.Default.Compare(other.ValueTo, this.ValueFrom) >= 0;
}
}
If you can't use the new switch expressions in this way, you can potentially do something like:
private static bool Foo(object rangeA, object rangeB)
{
return TryContainsInclusive<int>(rangeA, rangeB) ||
TryContainsInclusive<int?>(rangeA, rangeB);
}
private static bool TryContainsInclusive<T>(object a, object b)
{
if (a is Range<T> rangeA && b is Range<T> rangeB)
{
return rangeB.ContainsInclusive(rangeA);
}
return false;
}
If rangeA and rangeB can be generic types, you can get away with the simpler:
private static bool Foo<T>(Range<T> rangeA, Range<T> rangeB)
{
return rangeB.ContainsInclusive(rangeA);
}
If rangeA and rangeB can be some base Range type, then you can do something like this. Again, this will throw at runtime if T isn't comparable:
public class Program
{
public static void Main()
{
Foo(new Range<int>() { ValueFrom = 1, ValueTo = 10 }, new Range<int>() { ValueFrom = 0, ValueTo = 10 }).Dump();
Foo(new Range<int?>() { ValueFrom = 1, ValueTo = 10 }, new Range<int?>() { ValueFrom = 0, ValueTo = 10 }).Dump();
}
private static bool Foo(Range rangeA, Range rangeB)
{
return rangeB.ContainsInclusive(rangeA);
}
}
public abstract class Range
{
public abstract bool ContainsInclusive(Range other);
}
public class Range<T> : Range
{
public T ValueFrom { get; set; }
public T ValueTo { get; set; }
public override bool ContainsInclusive(Range other)
{
if (other is Range<T> o)
{
return Comparer<T>.Default.Compare(o.ValueFrom, this.ValueTo) <= 0 &&
Comparer<T>.Default.Compare(o.ValueTo, this.ValueFrom) >= 0;
}
return false;
}
}
You can also solve the problem by having a non-abstract base class Range. Both Range<int> and Range<int?> are then assignment compatible to this base class.
class Range
{
public object ValueFrom { get; protected set; }
public object ValueTo { get; protected set; }
}
class Range<T> : Range
{
public new T ValueFrom
{
get {
return (T)base.ValueFrom;
}
set {
base.ValueFrom = value;
}
}
public new T ValueTo
{
get {
return (T)base.ValueTo;
}
set {
base.ValueTo = value;
}
}
}
The properties of the generic class hide the properties of the base class. Their setter is protected. Therefore setting values is still type safe, because it can be done only through the generic class.
My solution then consists in testing the type of the From and To values. This also works well for nullable types. If a nullable is null, then nullable is int i yields false, otherwise if we have a Range<int?> it will assign nullable.Value to i.
This also allows you compare a Range<int> with a Range<int?>.
Range rangeA = new Range<int> { ValueFrom = 5, ValueTo = 12 };
Range rangeB = new Range<int?> { ValueFrom = 10, ValueTo = 18 };
if (rangeA.ValueFrom is int aFrom && rangeA.ValueTo is int aTo &&
rangeB.ValueFrom is int bFrom && rangeB.ValueTo is int bTo) {
return aFrom <= bTo && aTo >= bFrom;
}
return false;
A possible improvement is to have a strongly typed backing variable in the generic variant:
private T _valueFrom;
public new T ValueFrom
{
get {
return _valueFrom;
}
set {
base.ValueFrom = _valueFrom = value;
}
}
At least returning a value then involves no unboxing when working through the generic range.
Here's a simple class and a derived class:
class A { public int val; }
class B : A { }
I'd like a method Inc which works on both A and B instances that returns a new instance with val incremented by 1.
One way to approach this is to define Inc as an extension method:
static class Extensions
{
public static T Inc<T>(this T obj) where T : A, new()
{
return new T() { val = obj.val + 1 };
}
}
This seems to work. In the following example, a0.val is 11 and b0.val is 21.
var a = new A() { val = 10 };
var b = new B() { val = 20 };
var a0 = a.Inc();
var b0 = b.Inc();
My question is, is there a way to define Inc as a direct member of A and have it work as above?
If I define A like this:
class A
{
public int val;
public T Inc<T>() where T : A, new()
{
return new T() { val = val + 1 };
}
}
I then need to qualify the type when I call Inc:
var a = new A() { val = 10 };
var b = new B() { val = 20 };
var a0 = a.Inc<A>();
var b0 = b.Inc<B>();
Is there a way to go the member method route without having to qualify the type?
I don't think it's possible without implementing a new version of the method on each subclass, i.e,:
class A
{
public int val;
public virtual A Inc()
{
return new A { val = val + 1 };
}
}
class B : A
{
public new B Inc()
{
return new B { val = val + 1 };
}
}
So, you practically want to create clone of object with different value for some field:
class A {
public int val;
protected virtual A CloneInternal() {
return (A)MemberwiseClone();
}
public A Inc() {
A a=CloneInternal();
++a.val;
return a;
}
}
class B:A {
public new B Inc() {
return (B)base.Inc();
}
}
static void Main() {
A a=new B();
a=a.Inc();
Console.WriteLine(a.GetType());
}
Neither I don't think it's possible. You should provide a hint in order to make compiler guesses proper type for T.
You might try with static method:
public static T Inc<T>(T source) where T : A, new()
{
return new T() { val = source.val + 1 };
}
Then,
var b = new B { val = 20 };
var b0 = A.Inc(b);
But it's not an answer since you wanted a member method. I would rather go with extensions method.
abstract class Base
{
public int val { get; set; }
public virtual Base Inc() { return null; }
}
class A : Base
{
public override Base Inc()
{
return new A { val = val + 1 };
}
}
class B : A
{
public override Base Inc()
{
return new B { val = val + 2 };
}
}
Maybe using a abstract base class is better....
Base bClass = new B();
B bInc = bClass.Inc() as B;
One more way
interface IInc
{
int val { get; set; }
IInc GetNew();
}
class A : IInc
{
public int val
{
get;
set;
}
public virtual IInc GetNew()
{
return new A();
}
public IInc Inc()
{
var newObj = GetNew();
newObj.val++;
return newObj;
}
}
class B : A
{
public override IInc GetNew()
{
return new B();
}
}
and use like
var a = new A() { val = 10 };
var b = new B() { val = 20 };
var a0 = a.Inc();
var b0 = b.Inc();
Console.WriteLine(a0.val);
Console.WriteLine(b0.val);
Got Customer class which has Country property which has string property Name.
Also Customer implements IComparable<Country> like so:
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
Now:
var custList = new List<Customer>{...};
custList.OrderBy(cust => cust.Country).ToList(); //Sorts as charm.
And if try sorting via reflection:
var itemProp = typeof(Customer).GetProperty("Country");
custList = c.Customers.ToList()
.OrderBy(cust => itemProp.GetValue(cust, null)).ToList(); // Fails
Throws exception 'At least one object must implement IComparable'
Please explain why does it fail and how correctly implement sorting of Customer by custom property via reflection. Thanks.
Since GetValue returns Object you need to implement the non generic version of IComparable.
void Main()
{
var custList = new List<Customer>()
{
new Customer(){ Country = new Country(){ Name = "Sweden" } },
new Customer(){ Country = new Country(){ Name = "Denmark" } },
};
var itemProp = typeof(Customer).GetProperty("Country");
custList = custList.OrderBy(cust => itemProp.GetValue(cust, null)).ToList();
custList.Dump();
}
public class Country : IComparable<Country>, IComparable
{
public string Name {get;set;}
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
public int CompareTo(object other)
{
var o = other as Country;
if(o == null)
return 0; //Or how you want to handle it
return CompareTo(o);
}
}
public class Customer
{
public Country Country{get;set;}
}
Assuming that the underlying type is correct (i.e. Country), you should be able to do it as long as Country implements IComparable:
Here's a sample console app that works correctly (note that there is no error handling):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Number: IComparable<Number>, IComparable
{
public Number(int value)
{
Value = value;
}
public readonly int Value;
public int CompareTo(Number other)
{
return Value.CompareTo(other.Value);
}
public int CompareTo(object obj)
{
return CompareTo((Number) obj);
}
}
class Test
{
public Number Number;
public object Obj
{
get { return Number; }
}
public override string ToString()
{
return Number.Value.ToString();
}
}
internal static class Program
{
static void Main()
{
var itemProp = typeof(Test).GetProperty("Obj");
Console.WriteLine(string.Join("\n",
data().OrderBy(x => itemProp.GetValue(x, null))));
}
static IEnumerable<Test> data()
{
for (int i = 0; i < 10; ++i)
yield return new Test {Number = new Number(10-i)};
}
}
}
public class Item
{
public List<int> val { get; set; }
public double support { get; set; }
}
I declare variable:
List<Item> t = new List<Item>();
t.Add(new Item(){val = new List<int>(){1,2,3};support=.1);
var b = new Item();
b.val = t[0].val;
b.support=t[0].support;
t.Contain(b) // return false???
I'm try with linq
t.Any(a=>a.val==b.val) // I'm get error Expression cannot contain lambda expressions
3 possibilities come to mind:
You could implement IEquatable<T>:
public class Item: IEquatable<Item>
{
public List<int> val { get; set; }
public double support { get; set; }
public bool Equals(Item other)
{
return
this.support == other.support &&
this.val.SequenceEqual(other.val);
}
}
and now t.Contains(b) will return true.
If you cannot modify the Item class you could write a custom EqualityComparer:
public class ItemEqualityComparer : IEqualityComparer<Item>
{
private ItemEqualityComparer()
{
}
public static IEqualityComparer<Item> Instance
{
get
{
return new ItemEqualityComparer();
}
}
public bool Equals(Item x, Item y)
{
return
x.support == y.support &&
x.val.SequenceEqual(y.val);
}
public int GetHashCode(Item obj)
{
int hash = 27;
hash += (13 * hash) + obj.support.GetHashCode();
foreach (var item in obj.val)
{
hash += (13 * hash) + item.GetHashCode();
}
return hash;
}
}
and then t.Contains(b) will also return true.
Or if you prefer simply do it naively:
List<Item> t = new List<Item>();
t.Add(new Item { val = new List<int>(){1,2,3}, support=.1 });
var b = new Item();
b.val = t[0].val;
b.support = t[0].support;
bool equals = t.All(item => item.support == b.support && item.val.SequenceEqual(b.val));
Console.WriteLine(equals);
Your t.Any(a=>a.val == b.val) is correct.
The error you get is from the quick watch or expression window in the debugger, not from the compiler. Visual Studio's expression evaluator does not handle lambdas. However, it's still valid c# code, and will do what you want.
It's your earlier line that's a problem:
t.Add(new Item(){val = new List<int>(){1,2,3};support=.1);
This is a mixture of various different bits of syntax. It should be:
t.Add(new Item(){val = new List<int>(){1,2,3}, support=.1});
... although preferably with better property names, etc. Then the rest should work - although you need to do something with the result of Any. The Any call itself is valid. Here's a short but complete program which works:
using System;
using System.Collections.Generic;
using System.Linq;
public class Item
{
public List<int> Values { get; set; }
public double Support { get; set; }
}
class Test
{
static void Main()
{
List<Item> list = new List<Item>
{
new Item { Values = new List<int>{1, 2, 3},
Support = 0.1 }
};
var check = new Item { Values = list[0].Values,
Support = list[0].Support };
bool found = list.Any(a => a.Values == check.Values);
Console.WriteLine(found);
}
}
Note that this is performing a reference comparison between the two lists - if you created a different list with the same values (1, 2, 3), that wouldn't be found. You'd need to use a.Values.SequenceEqual(b.Values) or something similar.
Your Item class should implemenet the IEquatable interface:
class Item : IEquatable<Item>{
public List<int> val { get; set; }
public double support { get; set; }
public bool Equals(Item item){
return this.support.Equals(item.support) && this.val.SequenceEqual(item.val);
}
}
Then the Contains() method should work well.
You can correct your "t.Contain(b)" to t.Contains(b, new ItemEqualityComparer()) from System.Linq where ItemEqualityComparer will be your class, which will implement IEqualityComparer<Item>
How can I get only values when I use a JsonConvert.SerializeObject? I don't need repeat words like id, name, etc...
Example:{ id: 189, name:'Paul', age:31, } x { [189, 'Paul', 31] }
Thanks!
I need to use with PageList class
public class PageList {
IEnumerable _rows;
int _total;
int _page;
int _records;
object _userData;
public PageList(IEnumerable rows, int page, int total, int records, object userData) {
_rows = rows;
_page = page;
_total = total;
_records = records;
_userData = userData;
}
public PageList(IEnumerable rows, int page, int total, int records)
: this(rows, page, total, records, null) {
}
public int total { get { return _total; } }
public int page { get { return _page; } }
public int records { get { return _records; } }
public IEnumerable rows { get { return _rows; } }
[JsonIgnore]
public object userData { get { return _userData; } }
public override string ToString() {
return Newtonsoft.Json.JsonConvert.SerializeObject(this, new IsoDateTimeConverter() { DateTimeFormat = "dd-MM-yyyy hh:mm:ss" });
}
}
The closest thing I can think of is
var yourObjectList = List<YourObject>(){.....}
string s = JsonConvert.SerializeObject(GetObjectArray(yourObjectList));
public static IEnumerable<object> GetObjectArray<T>(IEnumerable<T> obj)
{
return obj.Select(o => o.GetType().GetProperties().Select(p => p.GetValue(o, null)));
}
The second one is not valid JSON ( { 189, 'Paul', 31 } ). Maybe you want an array instead ([ 189, 'Paul', 31 ]), in which case you can instead of using the serializer directly, first load the object into a JObject, then take only its values.
public class Foo
{
public int id;
public string name;
public int age;
}
public class Test
{
public static void Main()
{
Foo foo = new Foo { id = 189, name = "Paul", age = 31 };
JObject jo = JObject.FromObject(foo);
JArray ja = new JArray();
foreach (var value in jo.Values())
{
ja.Add(value);
}
Console.WriteLine(ja);
}
}
Or if you really want the non-JSON format, you can also use the JObject enumeration and print the values yourself.