views:

222

answers:

5
+1  Q: 

C# Combining lines

Hey everybody, this is what I have going on. I have two text files. Umm lets call one A.txt and B.txt.

A.txt is a config file that contains a bunch of folder names, only 1 listing per folder.

B.txt is a directory listing that contains folders names and sizes. But B contains a bunch of listing not just 1 entry.

What I need is if B, contains A. Take all lines in B that contain A and write it out as A|B|B|B ect....

So example:

A.txt:
Apple
Orange
Pear XBSj
HEROE

B.txt:
Apple|3123123
Apple|3434
Orange|99999999
Orange|1234544
Pear|11
Pear|12
XBSJ|43949
XBSJ|43933

Result.txt:
Apple|3123123|3434
Orange|99999999|1234544
Pear|11|12
XBSJ|43949|43933

This is what I had but it's not really doing what I needed.

string[] combineconfig = File.ReadAllLines(@"C:\a.txt");
        foreach (string ccline in combineconfig)
        {
            string[] readlines = File.ReadAllLines(@"C:\b.txt");
            if (readlines.Contains(ccline))
            {
                foreach (string rdlines in readlines)
                {
                    string[] pslines = rdlines.Split('|');
                    File.AppendAllText(@"C:\result.txt", ccline + '|' + pslines[0]);
                }
            }

I know realize it's not going to find the first "if" because it reads the entire line and cant find it. But i still believe my output file will not contain what I need.

+4  A: 

Assuming you're using .NET 3.5 (so can use LINQ), try this:

string[] configLines = File.ReadAllLines("a.txt");
var dataLines = from line in File.ReadAllLines("b.txt")
                let split = line.Split('|')
                select new { Key = split[0], Value = split[1] };
var lookup = dataLines.ToLookup(x => x.Key, x => x.Value);

using (TextWriter writer = File.CreateText("result.txt"))
{
    foreach (string key in configLines)
    {
        string[] values = lookup[key].ToArray();
        if (values.Length > 0)
        {
            writer.WriteLine("{0}|{1}", key, string.Join("|", values));
        }
    }
}
Jon Skeet
I gave this a try and at dataLines[key] i got Error 1 Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.IEnumerable<AnonymousType#1>'
Mike
change dataLines[key] to lookup[key] and it works perfect.
Zachary
according to the data, even with the change to lookup, it does not produce the wanted output, Jon, you fix? :) ie. "XBSj" is not handled correctly with the lowercase "j", and HEROE does not appear in the B.txt file, and thus is not part of the example result.
Lasse V. Karlsen
@Lasse: I'll fix the HEROE bit, but I'm not sure whether it's really meant to be case-insensitive and cope with multiple entries on a line. @Mike, can you speak to this?
Jon Skeet
Good point, I'll guess we'll have to see if he responds to what he means by it.
Lasse V. Karlsen
+5  A: 
var a = new HashSet<string>(File.ReadAllLines(@"a.txt")
                                .SelectMany(line => line.Split(' ')),
                            StringComparer.CurrentCultureIgnoreCase);

var c = File.ReadAllLines(@"b.txt")
            .Select(line => line.Split('|'))
            .GroupBy(item => item[0], item => item[1])
            .Where(group => a.Contains(group.Key))
            .Select(group => group.Key + "|" + string.Join("|", group.ToArray()))
            .ToArray();

File.WriteAllLines("result.txt", c);

Output:

Apple|3123123|3434
Orange|99999999|1234544
Pear|11|12
XBSJ|43949|43933
dtb
I tried to compile and gotThe best overloaded method match for 'string.Join(string, string[])Argument '2': cannot convert from 'string[][]' to 'string[]'The best overloaded method match for 'System.IO.File.WriteAllLines(string, string[])' has some invalid argumentsArgument '2': cannot convert from 'TResult[]' to 'string[]'All at group.ToArray())) . File.WriteAllLines("c.txt", c);
Mike
@Mike: Fixed. The value selector in the `GroupBy` clause was missing.
dtb
I tried your edit adn still produced the same errors in that location
Mike
The code now compiles but when it's ran it produces an IndexOutOfRangeException at Item[1]
Frank Hale
It compiles on my machine as well. If you get an IndexOutOfRangeException then your input is malformed.
dtb
Oops, yeah you are right. Ugh! I need to drink more coffee. =)
Frank Hale
By the way I love this solution!
Frank Hale
A: 

you need to enumerate readlines and check if readlines[i].StartsWith(cclines)

Al Bundy
+2  A: 

This should work:

using System;
using System.Linq;
using System.IO;
using System.Globalization;

namespace SO2593168
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = File.ReadAllLines("A.txt");
            var b =
                (from line in File.ReadAllLines("B.txt")
                 let parts = line.Split('|')
                 select new { key = parts[0], value = parts[1] });

            var comparer = StringComparer.Create(CultureInfo.InvariantCulture, true);
            var result =
                from key in a
                from keyvalue in b
                where comparer.Compare(keyvalue.key, key) == 0
                group keyvalue.value by keyvalue.key into g
                select new { g.Key, values = String.Join("|", g.ToArray()) };

            foreach (var entry in result)
                Console.Out.WriteLine(entry.Key + "|" + entry.values);
        }
    }
}

This produces:

Apple|3123123|3434
Orange|99999999|1234544
Pear|11|12
XBSJ|43949|43933

Code here.

Lasse V. Karlsen
+1  A: 

A short one :

var a = File.ReadAllLines("A.txt");
var b = File.ReadAllLines("B.txt");

var query =
    from bline in b
    let parts = bline.Split('|')
    group parts[1] by parts[0] into bg
    join aline in a on bg.Key equals aline
    select aline + "|" + string.Join("|", bg.ToArray());

File.WriteAllLines("result.txt", query.ToArray());
Thomas Levesque