split integer multiple values in one field into rows in ssis - c#

Please help me split column's field values into multiple rows.
Table
ID Name Location DeptNo
1 Jack Florida 101,102,103
I'm looking for output like this
ID Name Location DeptNo
1 Jack FLorida 101
1 Jack FLorida 102
1 Jack FLorida 103
I've figured out the configuration in ssis using script component but not sure about my code
Please check
public class ScriptMain : UserComponent
{
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
int[] Edpt = Row.DeptNo.ToInt().Split(new int[] { ',' }, IntSplitOptions.None);
int i = 0;
while (i < DeptNo.Length)
{
Output0Buffer.AddRow();
Output0Buffer.ID = Row.ID;
Output0Buffer.Name = Row.Name;
Output0Buffer.Location = Row.Location;
Output0Buffer.DeptNo = DeptNo[i];
i++;
}
}
}

99% of the way there.
Given a source like
SELECT
1 AS ID
, 'Jack' AS Name
, 'Florida' AS Location
, '101,102,103' AS DeptNo;
Your Script task becomes Asynchronous as it will not be a 1:1 input to output buffer. I made 3 changes to your script.
The first was in the creation of edpt array. There might be a way to split the strings and convert the result directly to a nullable integer array but it didn't come to mind.
string[] Edpt = Row.DeptNo.Split(new char[] { ',' });
The second changes was your for loop. while (i < DeptNo.Length) is going to look at each character in the source DeptNo string. so you'd have something like 11 output buffers created (which would then fail when it attempts to put the comma into an integer (unless it treats it as a char data type and then uses the ascii value). At any rate, to heck with while loops unless you need them. The foreach helps eliminate the dreaded off by one mistakes. So, I enumerate through my collection (Edpt) and for each value I find, I assign it to a loop scoped variable called item
foreach (var item in Edpt)
The final change is to the assignment in my output buffer. Output0Buffer.DeptNo = DeptNo[i]; again would only be access a specific value in the original string (1, 0, 1, ,, 1, 0, 2, ,, etc). Instead, you want to operate on the splitted array like Output0Buffer.DeptNo = Edpt[i]; But, since we don't need to do any of that ordinal access, we just reference item.
Output0Buffer.DeptNo = Int32.Parse(item);
The final code looks like
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
// Create an array of the department numbers as strings
string[] Edpt = Row.DeptNo.Split(new char[] { ',' });
// no longer needed
int i = 0;
// foreach avoids off by one errors
foreach (var item in Edpt)
{
Output0Buffer.AddRow();
Output0Buffer.ID = Row.ID;
Output0Buffer.Name = Row.Name;
Output0Buffer.Location = Row.Location;
// use the iterator directly
Output0Buffer.DeptNo = Int32.Parse(item);
}
}

Related

Effective way to loop without duplicate loop

I have a task to generate a file , so this is the example of data(from excel)
A Enr
B Cds
C Cdr
D Der
A Enr
B Cds
What i want is when you already found the same data, for example : The first row is A Enr, and the fifth row is A Enr, if you already loop the first row and find the same data (the fifth row) , i dont want when the variable int i = 4 , it will loop the A Enr again to search the same data ,because it already been search from i = 0 (which is the A ENR in the first row)
How to effective way to do the loop, because if you use the for statement, line 5 (A) will be looped back to check the same data, and that's the thing I don't want because on line 1 (A) has looped and found the same data in row 5.
Code Example
for (int i = 0; i < row; i++)
{
for(int k = 0 ; k < row ;k++)
{
if (fulldatadetail[i][0] == fulldatadetail[k][0])
{
if (!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
sw.WriteLine(fulldatadetail[i][0]);
}
}
else if (File.Exists(path))
{
using (StreamWriter sw = File.AppendText(path))
{
sw.WriteLine(fulldatadetail[i][0]);
}
}
}
}
}
P.S: sry for bad grammar, im trying to improve my english ..
it linq, group by, tuple does sound familiar to you, why don't try GroupBy?
// assume index is A,B,C,D
// assume value is Enr,Cds
var result = datas.GroupBy(x=> (x.index, x.value));
use foreach then print your key, your key in IGrouping<T> now is something like Tuple.
Item1 is index, Item2 is value.
foreach(var item in result.ToList())
{
something.Write(item.Key.Item1);
something.WriteLine(item.Key.Item2);
}
Try to declare an int/bool array with size according to your distinct elements (probably 22 as i see in this example),
Now, every row increase the cell corresponding to the latter that you see (e.g a[x-'A']++), also on every row do the inner loop only if the corresponding cell is 0.
So basicly just mark whenever you see a character so that the next time you see it you will know that you already saw one.
Provided you're trying to omit duplicate rows of data, and the only check you need is that the lines are completely equal (and not just based on the first character), this code may help you. It keeps track of every found value in a list and skips outputting it if it has already been output before.
string path = #"C:\Some\Text\File.txt";
List<string> outputValues = new List<string>
{
"A Enr",
"B Cds",
"C Cdr",
"D Der",
"A Enr",
"B Cds"
};
List<string> foundValues = new List<string>();
foreach (string outputValue in outputValues)
{
if (foundValues.Contains(outputValue))
continue; // Doesn't output this output value twice
foundValues.Add(outputValue);
using (StreamWriter sw = File.Exists(path) ? File.AppendText(path) : File.CreateText(path))
{
sw.WriteLine(outputValue);
}
}

How to get values from user using comma in c#?

using System;
namespace reverse
{
class Program
{
static void Main(string[] args)
{
int[] a = new int [10];
for (int i= 0; i<a.Length; i++)
{
a[i] = int.Parse(Console.ReadLine());
}
}
}
}
here I can get values from a user by pressing enter key after each time i give the value but I want to give value as a whole with comma. Thanks!
I would suggest to gradually step towards functional programming.
Why?
Weel, with words from Eric Lippert from "Functional programming for beginners"
I will talk about the “why” a little bit, but basically it boils down to
a great many bugs are caused by bad mutations.
By taking a page
from functional style and designing programs so that variables and
data structures change as little as possible, we can eliminate a
large source of bugs.
Structuring programs into small functions
whose outputs each depend solely on inputs
makes for programs that
are easy to unit test.
the ability to pass functions as data
allows us to create new and interesting control flows and patterns,
like LINQ.
Rewriting your code
Use Linq in a single and simple line:
int [] res =
Console.ReadLine () // read the input
.Split (',') // transform it into an array
.Take (10) // consider only the first 10 strings
.Select (int.Parse) // transform them into int
.ToArray (); // and finally get an array
You can add a check after the Split and before Take:
.Where (d => {int t; return int.TryParse (d, out t);}).Take
Try this one and read comments to get more info :
static void Main()
{
string input = Console.ReadLine(); // grab user input in one whole line
string[] splitted = input.Split(','); // split user input by comma sign ( , )
int[] a = new int[splitted.Length]; // create a new array that matches user input values length
for(int i = 0; i < splitted.Length; i++) // iterate through all user input values
{
int temp = -1; // create temporary field to hold result
int.TryParse(splitted[i], out temp); // check if user inpu value can be parsed into int
a[i] = temp; // assign parsed int value
}
}
This method will ensure that program will execute even if user wont input numerics. For example if user input will be :
1 , 2,3,45,8,9898
The output will be :
{ 1, 2, 3, 45, 8, 9898 }
But if the input will be :
1,adsadsa,13,4,6,dsd
The output will be :
{ 1, 0, 13, 4, 6, 0 }

Calculate variations on a string

I have a series of incorrectly encoded base36 values - these were encoded from integers using a string of letters, missing the "i" and "o". They now need to be converted back to integers using C#.
There are multiple permutations because of the rollover effect.
"0" can either equal 0 or 34;
"1" can either equal 1 or 35.
So, for instance, if I have a string "a110", it has six possible values.
I'm having a hard time trying to figure how to code for this.
All the examples I've looked at compute variations for a set of elements, for example
char[] = { a, b, c }
int[] = { 1, 2, 3 }
However, in my case, there are conditionals involved too, and it's making my head hurt. Can anyone help?
You can compute the list of all possible input strings. First, read the input into a list of ints. Now, you know that each of those (if it's a sufficiently low value) could be one of two things. So then you can create an enumerator that returns all of the possible inputs, via a recursive descent.
I managed to do it with the following code. It was actually a little simpler than I expected, since I only had two conditions, and two options. It uses recursion and steps through each character in the string. If that character is a 0 or 1, it then diverges, and continues building the string.
It actually generates a few duplicates, so I had to add a condition to only add it to the string list if it doesn't already exist. If someone else can point me to slightly better logic I'd appreciate it
public string st = "101"; // hardcoded for now
public char[] cs;
public List<string> variations;
static void Main()
{
cs = st.ToCharArray();
variations = new List<string>();
vary("",0);
}
static void vary(string m, int n)
{
for (int i = n; i < cs.Count(); i++)
{
if (cs[i] == '0' || cs[i] == '1')
{
// recurse
combo(m + (cs[i] == '0' ? "0" : "1"), i + 1);
combo(m + (cs[i] == '0' ? "Y" : "Z"), i + 1);
}
m += cs[i];
}
if(!variations.Contains(m))
variations.Add(m);
}
for the string "101" I get the following combinations
101
10Z
1Y1
1YZ
Z01
Z0Z
ZY1
ZYZ

Grouping CSV lines into one

I have a CSV file that has rows, where data for some columns only appear in one of those rows, with other columns repeating their value:
Heading1, Heading2, Heading3, Heading4
1 , 2 , , 4
1 , , 3 , 4
How can I end up with:
Heading1, Heading2, Heading3, Heading4
1 , 2 , 3 , 4
I want to group on Heading1 and Heading4 as they are unique to the repeated rows and get the first non-blank value for all other rows, ending up with a single string[].
I've got as far as grouping on new { Header1, Header4 } to get a group of rows, but I'm having a hard time turning that into something where I can select the first non null value for each column, then turning it back into a single row (string[]).
Ideally I'd like a function that works with any number of columns as in the actual file there are a large number.
It can be done with LINQ using Aggregate. Create a function to compare the running totals with the current row, setting the total for the column to the current value for the column if the total is not empty and the current value is not empty.
[TestMethod]
public void MergeArrays() {
string[] Input = new[] {
"H1, H2, H3, H4",
"1,2,,4",
"1,,3,4"
};
var header = Input.ElementAt(0) ;
var aggregation = string.Join(",", Input.Skip(1).Select(ln => ln.Split(',')).Aggregate(new[] { "", "", "", "" }, Agg));
var result = new string[] { header, aggregation };
Assert.AreEqual("H1, H2, H3, H4", header);
Assert.AreEqual("1,2,3,4", aggregation);
}
private static string[] Agg(string[] aggregation, string[] input) {
for (var idx = 0; idx < aggregation.GetLength(0); idx++) {
if (aggregation[idx] == string.Empty &&input[idx] != string.Empty){
aggregation[idx] = input[idx];
}
}
return aggregation;
}
hth,
Alan.
Make an array of length 4 with values initialized to some to empty strings (or zeros as appropriate).
For each non-header row, loop over the fields, storing the value in the corresponding array position whenever the field value is not blank.
Write-out the values in the array to the new CSV file.

Changing the order of a string based on an array of numbers in C#

Thanks for the help with my question about making an array of booleans into a string. The code is now working good. Now I have another problem. Maybe somebody would like to try. I think I could come up with a solution but if so I'm 99 percent sure that it would be not so simple as the answers I have seen from people here.
What I have is the string "ABD" from my question here. I also have an array of integers. For example [0] = 2, [1] = 3 and [2] = 1. I would like to find a way to apply this to my string to reorder the string so that the string changes to BDA.
Can anyone think of a simple way to do this?
If those integers are 1-based indices within the string (i.e. 2 = 2nd character), then you could do this:
string s = "ABD";
int[] oneBasedIndices = new [] { 2, 3, 1 };
string result = String.Join(String.Empty, oneBasedIndices.Select(i => s[i-1]));
NB: If you are using a version less than C# 4.0, you need to put a .ToArray() on the end of the select.
What this is doing is going through your int[] and with each int element picking the character in the string at that position (well -1, as the first index in an array is 0, but your example starts at 1).
Then, it has to do a String.Join() to turn that collection of characters back into a String.
As an aside, I'd recommend downloading LINQPad (no connection) - then you can just paste that code in there as a C# Program, and at any point type variable.Dump(); (e.g. result.Dump(); at the end) and see what the value is at that point.
First make a copy of the string. The copy will never change; it serves as your reference to what the values used to be.
Then loop through the original string one character at a time using a for loop. The counter in the for loop is the position of which character in the original string we are replacing next. The counter is also the index into the array to look up the position in the original string. Then replace the character at that position in the original string with the character from the copied string.
string orig = "ABD";
int[] oneBasedIndices = new [] { 2, 3, 1 };
string copy = orig;
for ( int i = 0; i < orig.Length; i++ )
{
orig[i] = copy[ oneBasedIndices[i] - 1 ];
}
There you have it. If the indices are zero based, remove the - 1.
Napkin code again...
string result = "ABD"; // from last question
var indecies = new []{ 1,2,0 };
string result2 = indecies.Aggregate(new StringBuilder(),
(sb, i)=>sb.Append(result[i]))
.ToString();
or a different version (in hopes of redeeming myself for -1)
StringBuilder sb = new StringBuilder();
for(int i = 0; i < indecies.Length; i++)
{
sb.Append(result[i]); // make [i-1] if indecies are 1 based.
}
string result3 = sb.ToString();

Categories

Resources