TransWikia.com

Get property name and value only if there is a value

Stack Overflow Asked by Jimenemex on November 28, 2020

I’m trying to simulate a .csv file with a list of property names as a header row followed by the actual list of properties for a list of items.

Currently, I have it working, but now there is a need to only include the properties’ header and value where the item has a value for that property anywhere in the list.

This is what I have currently:

public static string LeadsToCSVString(List<Lead> leads)
{
    StringBuilder builder = new StringBuilder();
    PropertyInfo[] properties = typeof(Lead).GetProperties();
    builder.AppendLine(string.Join(",", properties.Select(x => x.Name)));

    foreach (Lead lead in leads)
    {
        builder.AppendLine(string.Join(",", properties.Select(x => x.GetValue(lead))));
    }

    return builder.ToString();
}

Where Lead is:

public class Lead
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsOk { get; set; }
}

The List<Lead> isn’t guaranteed to have a value for all property values and there can even be a case where there is no value for a property like below:

FirstName,LastName,IsOK
Joe,,false
Adam,,true

What needs to be done is to only have header values and row column values where the property has an actual value. If I take the above example list, the CSV string would look like:

FirstName,IsOk
Joe,false
Adam,true

How can I modify my code to do this?

Some examples of what should happen if any object in the list has a value for the property:

FirstName,LastName,IsOK
Joe,Dirt,
Adam,,true

FirstName,LastName,IsOK
,Dirt,
Adam,,true

One Answer

Assuming that "has a value" means that its value is not null (which only works for reference types and Nullable value types), you could just filter out the ones that don't have any values in the list:

var filteredProps = properties.Where(p => leads.Any(l => p.GetValue(l) != null));

..and then use filteredProps instead of properties:

public static string LeadsToCSVString(List<Lead> leads)
{
    StringBuilder builder = new StringBuilder();
    PropertyInfo[] properties = typeof(Lead).GetProperties();

    var filteredProps = properties.Where(p => leads.Any(l => p.GetValue(l) != null));
    builder.AppendLine(string.Join(",", filteredProps.Select(x => x.Name)));

    foreach (Lead lead in leads)
    {
        builder.AppendLine(string.Join(",", filteredProps.Select(x => x.GetValue(lead))));
    }

    return builder.ToString();
}

Or to avoid calling GetValue() twice, you could create a dictionary (or a Lookup) of the properties and their values:

public static string LeadsToCSVString(List<Lead> leads)
{
    StringBuilder builder = new StringBuilder();
    PropertyInfo[] properties = typeof(Lead).GetProperties();

    var propsDictionary =
        properties.ToDictionary(p => p, p => leads.Select(l => p.GetValue(l)).ToArray())
        .Where(pair => pair.Value.Any(o => o != null))
        .ToDictionary(pair => pair.Key, pair => pair.Value);

    builder.AppendLine(string.Join(",", propsDictionary.Keys.Select(x => x.Name)));

    foreach (Lead lead in leads)
    {
        builder.AppendLine(
            string.Join(",", propsDictionary.Keys.Select(x => x.GetValue(lead))));
    }

    return builder.ToString();
}

Correct answer by 41686d6564 on November 28, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP