2

I have an array with different types of objects, for example:

Object[] array = {"String_1", double 3.14, "String_2"};

I want to override ToString() so when I use it on the array above it will show object class types and how many of them are in the array, for example:

Object[] array = {"String_1", double 3.14, "String_2"};

Console.WriteLine(array.ToString()); // will print "String-2,Double-1"

Would like some help with doing this :-)

  • 1
    Break the problem down - how would you _group_ the objects in the array by their _type_? And then how would you _count_ them? – mjwills May 16 '21 at 12:53
  • 3
    To be clear, you can't do this with `array.ToString()` (where array is the original array). You have to write some other code. – mjwills May 16 '21 at 12:54
  • Why would you want to build / print a string instead of properly using a dictionary? – OneCricketeer May 16 '21 at 13:05
  • You don't particularly need to use a dictionary @OneCricketeer - although that is one way yes. – mjwills May 16 '21 at 13:50
  • `string.Join(",", array.GroupBy(c => c?.GetType(), (c, i) => $"{c?.Name ?? "null"}-{i.Count()}"))` which can be simpler if you know you won't have nulls – pinkfloydx33 May 16 '21 at 18:12
  • @mjwills yeah thanks. I've mentioned that I need to override the original ToString().. – Michael Olari May 16 '21 at 19:35
  • @MichaelOlari Why do you want to override `ToString`? Who told you to do it that way? You don't have the ability to change `object[]`'s `ToString` method. _I mean you might be able to do it with some advanced techniques - but that is very much the wrong technique to use here._ – mjwills May 16 '21 at 21:16
  • @mjwills It's an assignment I've got in c# course. I actually managed to it(I was supposed to do it in a custom class of the Object array) :-). Thank you very much for all the tips and info! – Michael Olari May 17 '21 at 10:40
  • A custom class can work yes. But not `array.ToString()` _specifically_ (where array is the input array). – mjwills May 17 '21 at 10:52

3 Answers3

2

You cannot write your own ToString implementation for a class you do not own, and you do not own the Array class.

You'll need to write your own method that converts the array to your preferred format. Fortunately this is pretty straightforward with LINQ. We can even make it an extension method so it feels more natural:

using System.Linq;

public static class ArrayExt
{
   public static string ToCustomString(this object[] array) 
   {
      var grouped = array.GroupBy(
         c => c?.GetType(), 
         (type, vals) => $"{type?.Name ?? "null"}-{vals.Count()}"
      );
      var result = string.Join(",", grouped);
      return result;
   } 
} 

And an example of its usage:

object[] array = new object[] { 1, 2, "Three", 4.0f, "Five", 6M }; 
// extension method syntax 
var value1 = array.ToCustomString();
// or static invocation sytanx
var value2 = ToCustomString(array);

SharpLab

This groups the individual items in the array by their Type or null using GroupBy. Each group is transformed such that we now have a collection where each item is a string in the format "{TypeNameOrNull}-{Count}". We then pass this to string.Join to build a combined string, delimiting each item with a comma.

If you know you won't have nulls in your array, we can simplify the above to:

var grouped = array.GroupBy(
   c => c.GetType(),
   (type, vals) => $"{type.Name}-{vals.Count()}"
);
var result = string.Join(",", grouped);

If you (for example) want the final string ordered by the number of items per type/group, you'd need a more verbose version, combining GroupBy with OrderBy and Select:

var groupedAndOrdered = array
    .GroupBy(
       c => c.GetType(), 
       (type, vals) => new { Name = type.Name, Count = vals.Count() }
    )
    .OrderBy(c => c.Count)
    .Select(c => $"{c.Name}-{c.Count}");
var result = string.Join(",", groupedAndOrdered);

If you don't like the built-in type names (Single instead of float, Int32 instead of integer) you'd need to write a method that included the logic to turn them into your preferred text:

public static string TypeToName(Type type) {
    if (type == null) return "null";
    if (type == typeof(float)) return "float";
    if (type == typeof(int)) return "int";
    // etc 
    return type.Name; // fallback 
} 

Which you can then use when constructing the string, for example:

var grouped = array.GroupBy(
   c => c?.GetType(),
   (type, vals) => $"{TypeToName(type)}-{vals.Count()}"
);
var result = string.Join(",", grouped);
pinkfloydx33
  • 11,863
  • 3
  • 46
  • 63
0

For overriding, you need to derive from an array type. But you cannot even derive from array types in the first place. Even if you make ToString() an extension method, the default ToString() method will always be prioritized over an extension method. Your only option here is to create a custom array type with an overriden ToString().

public class CustomArray
{
    private object[] array;

    public CustomArray(int size) => array = new object[size]; 

    public CustomArray(object[] array) => this.array = array;
    
    public object this[int index] { get => array[index]; set => array[index] = value; } //Indexer 
    
    public override string ToString()
    {
          var dict = new Dictionary<Type, int>();
          foreach (var item in array) 
          {
              Type type = item.GetType();
              if (dict.ContainsKey(type)) 
              {
                  dict[type]++;
              } 
              else
              {
                 dict[type] = 1;  
              }
          }
          return string.Join(", ", dict.Select(item => $"{item.Key.Name}: {item.Value}"));
          
    } 
}

Of course, you can go more in depth and make your custom array implement IEnumerable<T>, ICollection<T> and other interfaces(see this answer) to make it more identical to the inbuilt array type.

Amal K
  • 4,359
  • 2
  • 22
  • 44
-1

Why you need to override the ToString?

Instead, write a function to StringfyObjectArray and Map the values in the desired format.

jithil
  • 1,098
  • 11
  • 11