.NET Fiddle

CSharp · Console

/Program.cs

using System;
using System.Data;
using System.Linq;
					
public class Program
{
	public static void Main()
	{
		DataTable dt = new DataTable();
		dt.Columns.Add("Id", typeof(int));
		dt.Columns.Add("Text", typeof(string));
		dt.Columns.Add("Value1", typeof(string));
		dt.Columns.Add("Value2", typeof(int));

		dt.Rows.Add(1, "Test1", "584", 12);
		dt.Rows.Add(2, "Test2", "32", 123);
		dt.Rows.Add(3, "Test3", "425", 54);
		dt.Rows.Add(4, "Test1", "4", 755);
		dt.Rows.Add(5, "Test5", "854", 879);
		dt.Rows.Add(6, "Test2", "1", null);
		dt.Rows.Add(7, "Test2", "999", 3);

		var duplicates = dt.Rows.OfType<DataRow>().GroupBy(x => x[1]).Where(x => x.Count() > 1).ToList();

		//get the current highestId (first column) so that when we remove duplicates and a new row the new row will get the next available id
		var highestId = dt.Rows.OfType<DataRow>().Max(x => (int)x[0]);

		//enumerate all duplicates
		foreach (var duplicate in duplicates)
		{
			//get the highest value of each column
			var newId = ++highestId;
			var newText = duplicate.Key;
			var newValue1 = duplicate.Max(x => x[2]); //this does a string comparison, instead of a numeric one, this means that for example that 2 is bigger then 10

			// use this if you need numeric comparison
			var newValue1AsNumeric = duplicate.Select(x =>
			{
				if (int.TryParse(Convert.ToString(x[2]), out var value))
					return value;

				return (int?)null;
			}).OfType<int>().Max(); 

			var newValue2 = duplicate.Select(x => x[3]).OfType<int>().Max();

			//enumerate each row of the duplicate
			foreach (var dataRow in duplicate)
				dt.Rows.Remove(dataRow);

			dt.Rows.Add(newId, newText, newValue1, newValue2);
		}
		
		
		foreach (DataRow dr in dt.Rows)
		{
			foreach (var item in dr.ItemArray)
			{
				Console.Write(item);
				Console.Write("|");
			}
			
			Console.WriteLine();
		}
	}
}
;