首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >按2列排序二维数组

按2列排序二维数组
EN

Stack Overflow用户
提问于 2022-04-25 07:52:12
回答 3查看 125关注 0票数 1

我正在寻找一种有效的方法来排序数据在一个二维数组。数组可以有许多行和列,但在本例中,我只将它限制为6行5列。数据是字符串,有些是单词。下面我只包含一个单词,但在实际数据中有几个字列。我意识到,如果我们分类,我们应该把数据当作数字吗?

代码语言:javascript
运行
复制
string[,] WeatherDataArray = new string[6,5];

这些数据是一组每天读取并记录的天气数据。这些数据通过他们系统的许多部分,我无法改变,它以一种需要排序的方式传递给我。一个示例布局可以是:

代码语言:javascript
运行
复制
Day number, temperature, rainfall, wind, cloud

数据矩阵可能如下所示

代码语言:javascript
运行
复制
3,20,0,12,cumulus
1,20,0,11,none
23,15,0,8,none
4,12,0,1,cirrus
12,20,0,12,cumulus
9,15,2,11,none

他们现在想要对数据进行排序,这样它就会有按降序排列的温度和按升序排列的日数。结果将是

代码语言:javascript
运行
复制
1,20,0,11,none
3,20,0,12,cumulus
12,20,0,12,cumulus
9,15,2,11,none
23,15,0,0,none
4,12,0,1,cirrus

数组被存储,稍后他们可以将其提取到一个表中,并对其进行大量分析。提取端没有改变,所以我不能对表中的数据进行排序,我必须以正确的格式创建数据,以匹配现有的规则。

我可以解析数组的每一行并对它们进行排序,但这似乎是一种非常冗长的方法。必须有一种更快、更有效的方法来按两列对这个二维数组进行排序?我想我可以将它发送到一个函数中,然后返回排序数组,如下所示:

代码语言:javascript
运行
复制
private string[,]  SortData(string[,] Data)
{

    //In here we do the sorting

}

有什么想法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-04-25 09:25:48

我同意另一个答案,即最好将每一行数据解析为封装数据的类的实例,从该数据创建一个新的一维数组或列表。然后对这个一维集合进行排序,并将其转换回一个2D数组。

但是,另一种方法是编写一个IComparer类,您可以使用它来比较2D数组中的两行,如下所示:

代码语言:javascript
运行
复制
public sealed class WeatherComparer: IComparer
{
    readonly string[,] _data;

    public WeatherComparer(string[,] data)
    {
        _data = data;
    }

    public int Compare(object? x, object? y)
    {
        int row1 = (int)x;
        int row2 = (int)y;

        double temperature1 = double.Parse(_data[row1, 1]);
        double temperature2 = double.Parse(_data[row2, 1]);

        if (temperature1 < temperature2)
            return 1;

        if (temperature2 < temperature1)
            return -1;

        int day1 = int.Parse(_data[row1,0]);
        int day2 = int.Parse(_data[row2,0]);

        return day1.CompareTo(day2);
    }
}

请注意,这包括对要排序的2D数组的引用,并在必要时解析用于排序的元素。

然后,您需要创建一个一维索引数组,这就是您要进行排序的内容。(不能对2D数组进行排序,但可以对引用2D数组行的一维索引数组进行排序。)

代码语言:javascript
运行
复制
public static string[,] SortData(string[,] data)
{
    int[] indexer = Enumerable.Range(0, data.GetLength(0)).ToArray();
    var comparer = new WeatherComparer(data);
    Array.Sort(indexer, comparer);

    string[,] result = new string[data.GetLength(0), data.GetLength(1)];

    for (int row = 0; row < indexer.Length; ++row)
    {
        int dest = indexer[row];

        for (int col = 0; col < data.GetLength(1); ++col)
            result[dest, col] = data[row, col];
    }

    return result;
}

然后可以调用SortData对数据进行排序:

代码语言:javascript
运行
复制
public static void Main()
{
    string[,] weatherDataArray = new string[6, 5]
    {
        { "3",  "20", "0", "12", "cumulus" },
        { "1",  "20", "0", "11", "none" },
        { "23", "15", "0", "8",  "none" },
        { "4",  "12", "0", "1",  "cirrus" },
        { "12", "20", "0", "12", "cumulus" },
        { "9",  "15", "2", "11", "none" }
    };

    var sortedWeatherData = SortData(weatherDataArray);

    for (int i = 0; i < sortedWeatherData.GetLength(0); ++i)
    {
        for (int j = 0; j < sortedWeatherData.GetLength(1); ++j)
             Console.Write(sortedWeatherData[i,j] + ", ");
            
        Console.WriteLine();
    }
}

输出:

代码语言:javascript
运行
复制
1, 20, 0, 11, none,
3, 20, 0, 12, cumulus,
12, 20, 0, 12, cumulus,
9, 15, 2, 11, none,
23, 15, 0, 8, none,
4, 12, 0, 1, cirrus,

请注意,此代码不包含任何错误检查-它假定数据中没有空值,并且所有解析的数据实际上都是可解析的。您可能需要添加适当的错误处理。

在.NET Fiddle:https://dotnetfiddle.net/mwXyMs上试一试

票数 0
EN

Stack Overflow用户

发布于 2022-04-25 09:12:59

我建议将数据解析为可以按常规方法排序的对象。比如使用LINQ:

代码语言:javascript
运行
复制
myObjects.OrderBy(obj => obj.Property1)
         .ThenBy(obj=> obj.Property2);

将数据表视为字符串只会使处理变得更加困难,因为在每一步中,您都需要解析值,处理潜在的错误,因为字符串可能是空的,或者包含无效的值等等。当数据被读取时,进行所有这些解析和错误处理,并在将数据写入磁盘或将其交给下一个系统时,将其转换为文本形式是一个更好的设计。

如果这是一个遗留系统,包含大量以文本形式处理数据的部分,我仍然主张先解析数据,然后在一个单独的模块中进行分析,这样就可以重用它。这应该允许部分重写其他部分使用对象格式。

如果这完全不可行,则需要将数据转换为锯齿数组,即string[][]。或者编写可以在多维数组中交换行的自己的排序。

票数 0
EN

Stack Overflow用户

发布于 2022-04-25 19:36:38

我很高兴尝试做一些比接受的答案更好的事情,我想我做到了。

理由是:

  • 用于排序和按升序或降序排序的列不是硬编码的,而是作为参数传入的。在这篇文章中,我了解到他们将来可能会改变对数据排序的想法。
  • 支持按不包含数字的列进行排序,因为如果它们想按名称列进行排序。在我的测试中,
  • 对于大数据来说,它要快得多,分配的内存也少得多。

原因是它更快:

  • 从未两次解析相同的数据索引。它缓存numbers.
  • When复制,它使用Span.CopyTo而不是indeces。
  • 它不创建一个新的数据数组,它在适当的位置对行进行排序。这也意味着它不会复制已经位于正确位置的行。

下面是用法:

代码语言:javascript
运行
复制
DataSorter.SortDataWithSortAguments(array, (1, false), (0, true));

这是密码:

代码语言:javascript
运行
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace YourNamespace;

public static class DataSorter
{
    public static void SortDataWithSortAguments(string[,] Data, params (int columnIndex, bool ascending)[] sortingParams)
    {
        if (sortingParams.Length == 0)
        {
            return;
            // maybe throw an exception instead? depends on what you want
        }

        if (sortingParams.Length > 1)
        {
            var duplicateColumns =
                from sortingParam in sortingParams
                group false by sortingParam.columnIndex
                into sortingGroup
                where sortingGroup.Skip(1).Any()
                select sortingGroup.Key;

            var duplicateColumnsArray = duplicateColumns.ToArray();
            if (duplicateColumnsArray.Length > 0)
            {
                throw new ArgumentException($"Cannot sort by the same column twice. Duplicate columns are: {string.Join(", ", duplicateColumnsArray)}");
            }
        }

        for (int i = 0; i < sortingParams.Length; i++)
        {
            int col = sortingParams[i].columnIndex;
            if (col < 0 || col >= Data.GetLength(1))
            {
                throw new ArgumentOutOfRangeException($"Column index {col} is not within range 0 to {Data.GetLength(1)}");
            }
        }

        int[] linearRowIndeces = new int[Data.GetLength(0)];
        for (int i = 0; i < linearRowIndeces.Length; i++)
        {
            linearRowIndeces[i] = i;
        }

        Span<int> sortedRows = SortIndecesByParams(Data, sortingParams, linearRowIndeces);

        SortDataRowsByIndecesInPlace(Data, sortedRows);
    }


    private static float[]? GetColumnAsNumbersOrNull(string[,] Data, int columnIndex)
    {
        if (!float.TryParse(Data[0, columnIndex], out float firstNumber))
        {
            return null;
        }

        // if the first row of the given column is a number, assume all rows of the column should be numbers as well

        float[] column = new float[Data.GetLength(0)];
        column[0] = firstNumber;

        for (int row = 1; row < column.Length; row++)
        {
            if (!float.TryParse(Data[row, columnIndex], out column[row]))
            {
                throw new ArgumentException(
                    $"Rows 0 to {row - 1} of column {columnIndex} contained numbers, but row {row} doesn't");
            }
        }

        return column;
    }

    private static Span<int> SortIndecesByParams(
        string[,] Data,
        ReadOnlySpan<(int columnIndex, bool ascending)> sortingParams,
        IEnumerable<int> linearRowIndeces)
    {
        var (firstColumnIndex, firstAscending) = sortingParams[0];

        var firstColumn = GetColumnAsNumbersOrNull(Data, firstColumnIndex);

        IOrderedEnumerable<int> sortedRowIndeces = (firstColumn, firstAscending) switch
        {
            (null, true) => linearRowIndeces.OrderBy(row => Data[row, firstColumnIndex]),
            (null, false) => linearRowIndeces.OrderByDescending(row => Data[row, firstColumnIndex]),
            (not null, true) => linearRowIndeces.OrderBy(row => firstColumn[row]),
            (not null, false) => linearRowIndeces.OrderByDescending(row => firstColumn[row])
        };

        for (int i = 1; i < sortingParams.Length; i++)
        {
            var (columnIndex, ascending) = sortingParams[i];

            var column = GetColumnAsNumbersOrNull(Data, columnIndex);

            sortedRowIndeces = (column, ascending) switch
            {
                (null, true) => sortedRowIndeces.ThenBy(row => Data[row, columnIndex]),
                (null, false) => sortedRowIndeces.ThenByDescending(row => Data[row, columnIndex]),
                (not null, true) => sortedRowIndeces.ThenBy(row => column[row]),
                (not null, false) => sortedRowIndeces.ThenByDescending(row => column[row])
            };
        }

        return sortedRowIndeces.ToArray();
    }

    private static void SortDataRowsByIndecesInPlace(string[,] Data, Span<int> sortedRows)
    {
        Span<string> tempRow = new string[Data.GetLength(1)];

        for (int i = 0; i < sortedRows.Length; i++)
        {
            while (i != sortedRows[i])
            {
                Span<string> firstRow = MemoryMarshal.CreateSpan(ref Data[i, 0], tempRow.Length);
                Span<string> secondRow = MemoryMarshal.CreateSpan(ref Data[sortedRows[i], 0], tempRow.Length);

                firstRow.CopyTo(tempRow);
                secondRow.CopyTo(firstRow);
                tempRow.CopyTo(secondRow);

                (sortedRows[i], sortedRows[sortedRows[i]]) = (sortedRows[sortedRows[i]], sortedRows[i]);
            }
        }
    }
}

PS:考虑到我的责任,我不应该花那么多时间在这上面工作,但这很有趣。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71996152

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档