<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Evbpc.Framework.Utilities.Serialization.DelimitedSerialization
{
    /// <summary>
    /// Represents a serializer that will serialize arbitrary objects to files with specific row and column separators.
    /// </summary>
    public class DelimitedSerializer
    {
        /// <summary>
        /// The string to be used to separate columns.
        /// </summary>
        public string ColumnDelimiter { get; set; }

        /// <summary>
        /// The string to be used to separate rows.
        /// </summary>
        public string RowDelimiter { get; set; }

        /// <summary>
        /// Serializes an object to a delimited file. Throws an exception if any of the property names, column names, or values contain either the <see cref="ColumnDelimiter"/> or the <see cref="RowDelimiter"/>.
        /// </summary>
        /// <typeparam name="T">The type of the object to serialize.</typeparam>
        /// <param name="items">A list of the items to serialize.</param>
        /// <returns>The serialized string.</returns>
        public string Serialize<T>(List<T> items)
        {
            if (string.IsNullOrEmpty(ColumnDelimiter))
            {
                throw new ArgumentException($"The property '{nameof(ColumnDelimiter)}' cannot be null or an empty string.");
            }

            if (string.IsNullOrEmpty(RowDelimiter))
            {
                throw new ArgumentException($"The property '{nameof(RowDelimiter)}' cannot be null or an empty string.");
            }

            var result = new ExtendedStringBuilder();

            var properties = typeof(T).GetProperties()
                .Where(x => Attribute.IsDefined(x, typeof(DelimitedColumnAttribute)))
                .OrderBy(x => ((DelimitedColumnAttribute)x.GetCustomAttributes(typeof(DelimitedColumnAttribute), true)[0]).Order)
                .ThenBy(x => ((DelimitedColumnAttribute)x.GetCustomAttributes(typeof(DelimitedColumnAttribute), true)[0]).Name)
                .ThenBy(x => x.Name);
            
            foreach (var property in properties)
            {
                var attribute = (DelimitedColumnAttribute)property.GetCustomAttributes(typeof(DelimitedColumnAttribute), true)[0];

                var name = attribute.Name ?? property.Name;
<commit_msg>Materialize `properties` for performance reasons.<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Evbpc.Framework.Utilities.Serialization.DelimitedSerialization
{
    /// <summary>
    /// Represents a serializer that will serialize arbitrary objects to files with specific row and column separators.
    /// </summary>
    public class DelimitedSerializer
    {
        /// <summary>
        /// The string to be used to separate columns.
        /// </summary>
        public string ColumnDelimiter { get; set; }

        /// <summary>
        /// The string to be used to separate rows.
        /// </summary>
        public string RowDelimiter { get; set; }

        /// <summary>
        /// Serializes an object to a delimited file. Throws an exception if any of the property names, column names, or values contain either the <see cref="ColumnDelimiter"/> or the <see cref="RowDelimiter"/>.
        /// </summary>
        /// <typeparam name="T">The type of the object to serialize.</typeparam>
        /// <param name="items">A list of the items to serialize.</param>
        /// <returns>The serialized string.</returns>
        public string Serialize<T>(List<T> items)
        {
            if (string.IsNullOrEmpty(ColumnDelimiter))
            {
                throw new ArgumentException($"The property '{nameof(ColumnDelimiter)}' cannot be null or an empty string.");
            }

            if (string.IsNullOrEmpty(RowDelimiter))
            {
                throw new ArgumentException($"The property '{nameof(RowDelimiter)}' cannot be null or an empty string.");
            }

            var result = new ExtendedStringBuilder();

            var properties = typeof(T).GetProperties()
                .Where(x => Attribute.IsDefined(x, typeof(DelimitedColumnAttribute)))
                .OrderBy(x => ((DelimitedColumnAttribute)x.GetCustomAttributes(typeof(DelimitedColumnAttribute), true)[0]).Order)
                .ThenBy(x => ((DelimitedColumnAttribute)x.GetCustomAttributes(typeof(DelimitedColumnAttribute), true)[0]).Name)
                .ThenBy(x => x.Name)
                .ToList();
            
            foreach (var property in properties)
            {
                var attribute = (DelimitedColumnAttribute)property.GetCustomAttributes(typeof(DelimitedColumnAttribute), true)[0];

                var name = attribute.Name ?? property.Name;

{'fraction_non_alphanumeric': 0.11106574111882402, 'fraction_numerical': 0.0012249897917517355, 'mean_word_length': 2.3554794520547944, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2773837', 'n_tokens_mistral': 1235, 'n_tokens_neox': 1231, 'n_words': 361}