NullReference
NullReference

Reputation: 4484

Is there a better way to split this string using LINQ?

I've got some longitude\latitude coordinates bunched together in a string that I'd like to split into longitude\latitude pairs. Thanks to stackoverflow I've been able to come up with the some linq that will split it into a multidimensional string array. Is there a way to split the string directly into an object that accepts the longitude latitude vs a string array then create the object?

string segment = "51.54398, -0.27585;51.55175, -0.29631;51.56233, -0.30369;51.57035, -0.30856;51.58157, -0.31672;51.59233, -0.3354"

string[][] array = segment.Split(';').Select(s => s.Split(',')).ToArray();
foreach (string[] pair in array)
{
//create object here
}

Upvotes: 9

Views: 43861

Answers (7)

GSoft Consulting
GSoft Consulting

Reputation: 718

Some tasks are just easier to solve the old way:

var split = segment.Split();
var coordinates = new List<Coordinate>(split.Length);
foreach(string s in split)
{
    coordinates.Add(new Coordinate(s));
}

Upvotes: 1

sehe
sehe

Reputation: 392911

Here is a ‘somewhat’ nice snippet showing:

  • precompiled regexen
  • LINQ to anonymous type projection
  • Culture-aware (correct) number parsing and printing

You would want to extract certain code (e.g. the number parsing) in real life.

See it live on Ideone.com.

using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Globalization;

namespace SODemo
{
    class MainClass
    {
        private static readonly CultureInfo CInfo = CultureInfo.CreateSpecificCulture("en-US");

        public static void Main (string[] args)
        {
            string segment = "51.54398, -0.27585;51.55175, -0.29631;51.56233, -0.30369;51.57035, -0.30856;51.58157, -0.31672;51.59233, -0.3354";

            var re = new Regex(@"\s*(?<lat>[-+]?[0-9.]+),\s*(?<lon>[-+]?[0-9.]+)\s*;", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

            var locations = re.Matches(segment).Cast<Match>().Select(m => new 
            {
                Lat  = decimal.Parse(m.Groups["lat"].Value, CInfo),
                Long = decimal.Parse(m.Groups["lon"].Value, CInfo),
            });

            foreach (var l in locations)
                Console.WriteLine(l);
        }
    }
}

Output:

{ Lat = 51,54398, Long = -0,27585 }
{ Lat = 51,55175, Long = -0,29631 }
{ Lat = 51,56233, Long = -0,30369 }
{ Lat = 51,57035, Long = -0,30856 }
{ Lat = 51,58157, Long = -0,31672 }

Upvotes: 3

Ethan Brown
Ethan Brown

Reputation: 27282

You could do this:

public class GeoCoordinates {
  public decimal Latitude { get; set; }
  public decimal Longitude { get; set; }

  public GeoCoordinates( string latLongPair ) {
    decimal lat, lng;
    var parts = latLongPair.Split( new[] { ',' } );
    if( decimal.TryParse( parts[0], out lat ) &&
      decimal.TryParse( parts[1], out lng ) ) {
      Latitude = lat;
      Longitude = lng;
    } else {
      // you could set some kind of "ParseFailed" or "Invalid" property here
    }
  }
}

Then you can create a collection of GeoCoordinate classes thusly:

var coords = segment.Split( new[] {';'} ).Select( x => new GeoCoordinates( x ) );

Upvotes: 3

dtb
dtb

Reputation: 217263

Assuming you have a Coordinate class with a public Coordinate(double x, double y) constructor, you can do this:

Coordinate[] result = segment
    .Split(';')
    .Select(s => s.Split(','))
    .Select(a => new Coordinate(x: double.Parse(a[0], NumberStyles.Number),
                                y: double.Parse(a[1], NumberStyles.Number))
    .ToArray();

or equally

var query = from item in segment.Split(';')
            let parts = item.Split(',')
            let x = double.Parse(parts[0], NumberStyles.Number)
            let y = double.Parse(parts[1], NumberStyles.Number)
            select new Coordinate(x, y);

Coordinate[] result = query.ToArray();

Upvotes: 6

SouthShoreAK
SouthShoreAK

Reputation: 4296

I might add a bit more. Thanks to dtb for the start, upvoted. If you break your parsing function out, you can more cleanly handle error conditions, such as wrong number of elements in your array, or things that don't parse to a decimal.

Coordinate[] result = segment
.Split(';')
.Select(s => s.Split(','))
.Select(BuildCoordinate)
.ToArray();

Coordrinate BuildCoordinate(string[] coords)
{
    if(coords.Length != 2)
        return null;

    return new Coordinate(double.Parse(a[0].Trim(), double.Parse(a[1]);
}

Upvotes: 1

Adam V
Adam V

Reputation: 6356

Is it a necessity that you use LINQ? You can do it all with standard string splitting functionality:

string[] pairsOfCoords = segment.Split(';');
List<CoordsObject> listOfCoords = new List<CoordsObject>();
foreach (string str in pairsOfCoords)
{
  string[] coords = str.Split(',');
  CoordsObject obj = new CoordsObject(coords[0], coords[1]);
  listOfCoords.Add(obj);
}

Upvotes: 2

Mehrdad Afshari
Mehrdad Afshari

Reputation: 421978

You are close. Something like this might help:

var pairSequence = segment.Split(';')
        .Select(s => s.Split(','))
        .Select(a => new { Lat = double.Parse(a[0]), Long = double.Parse(a[1]) });

Upvotes: 24

Related Questions