Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort a string by Linq

Tags:

string

c#

linq

Let's say I have the following input:

string input = "123456789";

and expect the following output:

string output = "741852963";

The logic is a square which needs to be turned by 90 degrees to the right - but without linebreak.

//  squares
//INPUT     OUTPUT
// 123  ══╗  741
// 456    V  852
// 789       963

The width should be dynamic and always

int width = (int)Math.Sqrt(input.Length);

Is there a easy approach to solve this?

like image 394
Byyo Avatar asked Nov 03 '15 13:11

Byyo


4 Answers

personally I would prefer for loop, but there is a Linq solution also

try it with Fiddle

string input = "0123456789ABCDEF";
/*
    0123
    4567
    89AB
    CDEF
*/              

int width = (int)Math.Sqrt(input.Length);

var seq = input.AsEnumerable()
                .Select((c, i) => new {Chr = c, Row = i / width, Col = i % width})
                .OrderBy(a => a.Col)
                .ThenByDescending(a => a.Row)
                .Select(a=>a.Chr);
var s = string.Join("", seq);
Console.WriteLine(s);

prints

C840D951EA62FB73
like image 82
ASh Avatar answered Oct 10 '22 13:10

ASh


How about:

void Main()
{
    var input = "123456789";
    var size = (int)Math.Sqrt(input.Length);
    var table = new char[size, size];

    for (var i = 0; i < input.Length; ++i)
    {
        table[i / size, i % size] = input[i];
    }

    var result = new StringBuilder();

    for (var i = 0; i < size; ++i)
    {
        for (var j = size - 1; j >= 0; --j)
        {
            result.Append(table[j, i]);
        }
    }

    Console.WriteLine(result.ToString());
}
like image 29
Kvam Avatar answered Oct 10 '22 13:10

Kvam


Well I had fun with this one:

const string input = "123456789";
string toFill = "";
int offset = 0;
int upTo = (int) Math.Sqrt(input.Length);
while (offset < upTo )
{
    string temp = "";

    for (int i = 0; i < input.Length; i = i + upTo)
    {
        temp += input[i + offset];
    }
    offset++;
    toFill += new string(temp.Reverse().ToArray()); ;
}

It works but don't use linq :(

like image 39
Thomas Ayoub Avatar answered Oct 10 '22 13:10

Thomas Ayoub


Sort solution provided by ASh is perfect, there is nothing to add there.

But there is an interesting comment by Enigmativity saying "This is more of a mapping than a sort", so let give it a try.

What we have here is

int length = input.Length;
int width = (int)Math.Sqrt(length);
int height = (length + width - 1) / width;

and for each index i1 in range [0, length-1] we define a transformation to another index i2 in the same range as follows

int x1 = i1 % width;
int y1 = i1 / width;
int x2 = height - 1 - y1;
int y2 = x1;
int i2 = y2 * height + x2;

Now let make the inverse transformation of the above

int x2 = i2 % height;
int y2 = i2 / height;
int x1 = y2;
int y1 = height - 1 - x2;
int i1 = width * y1 + x1;

which after substitutions becomes

int i1 = width * (height - 1 - i2 % height) + i2 / height;

With all that in mind, here is the "map" Linq solution:

string input = "123456789";
int length = input.Length;
int width = (int)Math.Sqrt(length);
int height = (length + width - 1) / width;
var output = new string(
    Enumerable.Range(0, length)
    .Select(i => input[width * (height - 1 - i % height) + i / height])
    .ToArray());

Of course LINQ is no sense here, we can avoid introducing closures and do the same with the same or less amount of "normal" code lines, and as a bonus having a sweet choice of using the inverse transformation as above

var result = new char[length];
for (int i = 0; i < result.Length; i++)
    result[i] = input[width * (height - 1 - i % height) + i / height];
var output = new string(result);

or the direct transformation (after substitution and simplification)

for (int i = 0; i < result.Length; i++)
    result[height * (i % width + 1) - (i / width + 1)] = input[i];
like image 3
Ivan Stoev Avatar answered Oct 10 '22 13:10

Ivan Stoev