I have a list like this,
List A
ItemNum FileName
001 A.txt,B.txt,A.txt,B.txt
002 A.txt,C.txt,A.txt,C.txt
I need to make a list like this.
ItemNum FileName
001 A.txt,B.txt
002 A.txt,C.txt
Is there any way to do it?
Case sensitive, in a method format:
public List<string> ToDistinct(IEnumerable<string> input)
{
List<string> unique = new List<string>();
foreach (string s in input)
{
List<string> files = s.Split(',').ToList();
unique.Add(String.Join(",", files.Distinct()));
}
return unique;
}
Here's a console method that displays the output like you have above and you can tweak it if you like:
public static void Main(string[] args)
{
List<string> input = new List<string>{"A.txt,B.txt,A.txt,B.txt", "A.txt,C.txt,A.txt,C.txt"};
//Display Input
Console.WriteLine("Input");
Console.WriteLine("ItemNum FileNames");
for(int i = 0; i < input.Count(); i++)
{
Console.WriteLine(String.Format(" {0,-23:000}{1}", i + 1, input[i]));
}
//Build the Unique List
List<string> unique = new List<string>();
foreach (string s in input)
{
List<string> files = s.Split(',').ToList();
unique.Add(String.Join(",", files.Distinct()));
}
//Display Output
Console.WriteLine();
Console.WriteLine("Output");
Console.WriteLine("ItemNum FileNames");
for(int i = 0; i < unique.Count(); i++)
{
Console.WriteLine(String.Format(" {0,-23:000}{1}", i + 1, unique[i]));
}
Console.ReadKey();
}
I'm going to suggest that you convert the FileName's to a List<string>, then all you need is this:
listA.ForEach(x => x.FileNames = x.FileNames.Distinct().ToList());
If there is no specific reason why you're storing what is - for all intents - a list in a string.. why not store it as one? List<string> will allow you to add and remove as you see fit.
List<Item> list = new List<Item>();
list.Add(new Item("001", "A.txt,B.txt,A.txt,B.txt"));
list.Add(new Item("002", "A.txt,C.txt,A.txt,C.txt"));
var newList = from l in list
select new Item() { ItemNum = l.ItemNum,
FileName = string.Join(",", l.FileName.Split(',').Distinct()) };
Related
Looking for alternative code in Java8/streams.
I want to copy specific values from a Map into a List using a predefined array of Keys.
The code to accomplish this task in Java 7 is as follows:
public List<Fruit> getFruitList(Map<String, Fruit> fruitMap) {
final String[] fruitNames = { "apple", "banana", "mango" };
final ArrayList<Fruit> fruitList = new ArrayList<>(fruitNames.length);
for (int i = 0; i < fruitNames.length; i++) {
final String fruitName = fruitNames[i];
final Fruit fruit = fruitMap.get(fruitName);
if (fruit != null) {
fruitList.add(fruit);
}
}
fruitList.trimToSize();
return fruitList;
}
Figured out a possible solution myself:
return Stream.of(fruitNames)
.map(fruitMap::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
I have the following custom class
public class Album
{
public string PhotoName { get; set; }
public string Location { get; set; }
public DateTime DateTime { get; set; }
}
and I have the following string:
#"photo.jpg, Warsaw, 2013-09-05 14:08:15
john.png, London, 2015-06-20 15:13:22
myFriends.png, Warsaw, 2013-09-05 14:07:13
Eiffel.jpg, Paris, 2015-07-23 08:03:02
pisatower.jpg, Paris, 2015-07-22 23:59:59
BOB.jpg, London, 2015-08-05 00:02:03"
and I need to write a function that will append the order number beside the Location based on the timestamp thus the resulting StringBuilder must be
Warsaw01.jpg
London01.jpg
Warsaw02.jpg
Paris01.jpg
Paris02.jpg
London02.jpg
What I have done so far?
I have a List of that type that I sorted by Location then by DateTime
List<Album> SortedList = list
.OrderBy(o => o.Location)
.ThenBy(o => o.DateTime)
.ToList();
now I need to have a StringBuilder that will append the index number beside the location.
This is my complete method with the part and I am stuck on how should I search the ordered list. Question is: how can I write the LINQ for searching through the list?:
public static string Solution(string S)
{
string[] group = S.Split("\r\n");
List<Album> list = new List<Album>();
StringBuilder sb = new StringBuilder();
//added each line of the string to list
foreach (string g in group)
{
string[] album = g.Split(',');
Album a = new Album();
a.PhotoName = album[0];
a.Location = album[1];
a.DateTime = DateTime.Parse(album[2]);
list.Add(a);
}
//ordered the list
List<Album> SortedList = list.OrderBy(o => o.Location).ThenBy(o => o.DateTime).ToList();
//then foreach line, append index number by searching through the list
foreach (string g in group)
{
string[] album = g.Split(',');
Album a = new Album();
a.PhotoName = album[0];
string[] photodetails = a.PhotoName.Split('.');
a.Location = album[1];
a.DateTime = DateTime.Parse(album[2]);
//this is the part where I must figure out how to build the string. I am stuck here
// var query = SortedList.IndexOf(list.SingleOrDefault(i => i.DateTime == a.DateTime));
sb.AppendLine(a.Location + query + "." + photodetails[1]);
}
string res = sb.ToString();
return res;
}
Appreciate the responses.
Update Warsaw2 must appear before Warsaw1 since the timestamp of Warsaw2 is later than Warsaw1
Warsaw02.jpg
London01.jpg
Warsaw01.jpg
Paris01.jpg
Paris02.jpg
London02.jpg
I have just added a order in the Album class
public class Album
{
public string PhotoName { get; set; }
public string Location { get; set; }
public DateTime DateTime { get; set; }
public int Order { get; set; }
}
public static string Solution(string S)
{
string[] stringSeparators = new string[] { "\r\n" };
string[] group = S.Split(stringSeparators, StringSplitOptions.None);
List<Album> list = new List<Album>();
StringBuilder sb = new StringBuilder();
//added each line of the string to list
for (int i = 0; i < group.Length; i++)
{
string[] album = group[i].Split(',');
Album a = new Album();
a.PhotoName = album[0];
a.Location = album[1];
a.DateTime = DateTime.Parse(album[2]);
a.Order = i;
list.Add(a);
}
//ordered the list
var groupedByLocation = list.GroupBy(o => o.Location).ToList();
for (int i = 0; i < groupedByLocation.Count; i++)
{
int indexValue = 01;
foreach (var item in groupedByLocation[i])
{
item.PhotoName = string.Format("{0}{1}.jpg", groupedByLocation[i].Key, indexValue);
indexValue++;
}
}
//then foreach line, append index number by searching through the list
var locations = groupedByLocation
.SelectMany(g => g.Select(h => h))
.ToList()
.OrderBy(y => y.Order)
.Select(g => g.PhotoName);
return string.Join("\r\n", locations);
}
Just for the fun - an alternative approach:
For the task at hand, there's no need to have an Album class, a list and two loops.
We can go over the lines once, and use a dictionary to hold the counters for us.
class PhotoLocationsCounter
{
private readonly Dictionary<string, int> locationsCounter = new Dictionary<string, int>();
public string GetLocationsWithCounters(string source)
{
string[] lines = source.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
var locations = lines.Select(this.LineToLocationWithCounter);
return string.Join("\n", locations);
}
private string LineToLocationWithCounter(string line)
{
string[] album = line.Split(',');
var location = album[1].Trim();
var ext = album[0].Split('.')[1];
var counter = this.GetAndIncreaseLocationCounter(location);
return $"{location}{counter.ToString("D2")}.{ext}";
}
private int GetAndIncreaseLocationCounter(string location)
{
if (!this.locationsCounter.TryGetValue(location, out int counter))
{
this.locationsCounter.Add(location, 0);
}
return ++this.locationsCounter[location];
}
}
And you call it:
string data = #"photo.jpg, Warsaw, 2013-09-05 14:08:15
john.png, London, 2015-06-20 15:13:22
myFriends.png, Warsaw, 2013-09-05 14:07:13
Eiffel.jpg, Paris, 2015-07-23 08:03:02
pisatower.jpg, Paris, 2015-07-22 23:59:59
BOB.jpg, London, 2015-08-05 00:02:03";
var locations = new PhotoLocationsCounter().GetLocationsWithCounters(data);
Lets say I have a list of asteroid objects like so:
9_Amphitrite
24_Themis
259_Aletheia
31_Euphrosyne
511_Davida
87_Sylvia
9_Metis
41_Daphne
Each asteroid has a title, a StartRoationPeriod, and a EndRoationPeriod.
I need to concatenate their names based on how close the current asteroid StartRoationPeriod and previous asteroid EndRoationPeriod are to an orbital constant and then spit out the concatenated title.
So with the above list, the final objects may look like this:
9_Amphitrite
24_Themis;259_Aletheia
31_Euphrosyne;511_Davida;87_Sylvia
9_Metis
41_Daphne
This requires me to keep track of both the current and previous asteroids.
I started to write the loop, but I'm unsure of where or even how to check the current asteroids start rotation period against the previous asteroids end rotation period...basically, it just gets messy fast...
string asteroid_title = string.Empty;
Asteroid prev_asteroid = null;
foreach (var asteroid in SolarSystem)
{
if (prev_asteroid != null)
{
if (asteroid.StartRoationPeriod + OrbitalConstant >= prev_asteroid.EndRoationPeriod)
{
asteroid_title = asteroid_title + asteroid.Title;
} else {
asteroid_title = asteroid.Title;
yield return CreateTitle();
}
}
prev_evt = evt;
}
I think this should work for you (If aggregate looks too complex try to convert it to a foreach,it's easy)
using System;
using System.Collections.Generic;
using System.Linq;
namespace Program
{
class Asteroid
{
public int EndRoationPeriod { get; internal set; }
public string Name { get; internal set; }
public int StartRoationPeriod { get; internal set; }
}
class AsteroidGroup
{
public int EndRoationPeriod { get; internal set; }
public string Names { get; internal set; }
}
internal class Program
{
private static void Main(string[] args)
{
int OrbitalConstant = 10;
List<Asteroid> SolarSystem = new List<Asteroid>()
{
new Asteroid() { Name= "9_Amphitrite" ,StartRoationPeriod=10 ,EndRoationPeriod=50},
new Asteroid() { Name= "24_Themis" ,StartRoationPeriod=45,EndRoationPeriod=100},
new Asteroid() { Name= "259_Aletheia",StartRoationPeriod=40 ,EndRoationPeriod=150},
new Asteroid() { Name= "31_Euphrosyne" ,StartRoationPeriod=60,EndRoationPeriod=200},
new Asteroid() { Name= "511_Davida" ,StartRoationPeriod=195,EndRoationPeriod=250},
new Asteroid() { Name= "87_Sylvia" ,StartRoationPeriod=90,EndRoationPeriod=300},
new Asteroid() { Name= "9_Metis" ,StartRoationPeriod=100,EndRoationPeriod=350},
new Asteroid() { Name= "41_Daphne" ,StartRoationPeriod=110,EndRoationPeriod=400},
};
var result = //I skip the first element because I initialize a new list with that element in the next step
SolarSystem.Skip(1)
//The first argument of Aggregate is a new List with your first element
.Aggregate(new List<AsteroidGroup>() { new AsteroidGroup { Names = SolarSystem[0].Name, EndRoationPeriod = SolarSystem[0].EndRoationPeriod } },
//foreach item in your list this method is called,l=your list and a=the current element
//the method must return a list
(l, a) =>
{
//Now this is your algorithm
//Should be easy to undrestand
var last = l.LastOrDefault();
if (a.StartRoationPeriod + OrbitalConstant >= last.EndRoationPeriod)
{
last.Names += " " + a.Name;
last.EndRoationPeriod = a.EndRoationPeriod;
}
else
l.Add(new AsteroidGroup { Names = a.Name, EndRoationPeriod = a.EndRoationPeriod });
//Return the updated list so it can be used in the next iteration
return l;
});
A more compact solution
var result = SolarSystem
.Skip(1)
.Aggregate( SolarSystem.Take(1).ToList(),
(l, a) => (a.StartRoationPeriod + OrbitalConstant >= l[l.Count - 1].EndRoationPeriod) ?
(l.Take(l.Count - 1)).Concat(new List<Asteroid> { new Asteroid() { Name = l[l.Count - 1].Name += " " + a.Name, EndRoationPeriod = a.EndRoationPeriod } }).ToList() :
l.Concat(new List<Asteroid> { a }).ToList()
);
Edit: At first I thought this was related to component views, but I've managed to isolate the issue a bit.
I have the following class:
public class TmpContext
{
public NYPContext db { get; set; }
public IEnumerable<SelectListItem> GetUserListFromSelection(int[] selection)
{
var userList = from u in db.UsuariosIntranet
join su in selection on u.Id equals su
select new SelectListItem()
{
Text = string.Format("{0}, {1}", u.ApellidoPaterno, u.Nombres),
Value = u.Id.ToString(),
};
return userList;
}
}
I have a view that receives the class above as model, and have the following code in it:
#{
// First list
var list1 = new int[] { 4947850 };
var a = Model.GetUserListFromSelection(list1);
foreach (var user in a)
{
<p class="tag">#user.Text</p>
}
// Second list, note the different ids
var list2 = new int[] { 2, 3 };
var b = Model.GetUserListFromSelection(list2);
foreach (var user in b)
{
<p class="tag">#user.Text</p>
}
// Third list
var o = list1.ToList();
// add a new id
o.Add(5185969);
// int[] otra = ;
var c = Model.GetUserListFromSelection(o.ToArray());
foreach (var user in c)
{
<p class="tag">#user.Text</p>
}
}
The expected results are three different lists, but somehow I get the first items repeated three times.
Is this expected behaviour?
It appears to be a bug, and it is already fixed. Talk about fast turnaround!
https://github.com/aspnet/EntityFramework/issues/2826
I will call AnalyseLinqUpdate()
I think code itself clear..
I have to find behavior for each dictionary value and replace the value with the behavior I get from the method 'GiveBehavior'
void AnalyseLinqUpdate()
{
Dictionary<string, string> rawCollection = new Dictionary<string, string>();
rawCollection.Add("PT-1", "PTC-1");
rawCollection.Add("PT-2", "PTC-1");
rawCollection.Add("PT-3", "PTC-2");
rawCollection.Add("PT-4", "PTC-2");
rawCollection.Add("PT-5", "PTC-3");
rawCollection.Add("PT-6", "PTC-3");
//update here
// call GiveBehavior("PTC-1");
//returns a string that needs to be updated in place of "PTC-1"
}
string GiveBehavior(string ptc)
{
StringComparison ignoreCase = StringComparison.OrdinalIgnoreCase;
ptc = ptc.Trim();
if (ptc.Equals("PTC-1", ignoreCase))
{
return "PTB-1";
}
else if (ptc.Equals("PTC-1", ignoreCase))
{
return "PTB-2";
}
else
{
return "PTB-3";
}
}
Currently I have done like:
List<string> keys = rawCollection.Keys.ToList();
foreach (string key in keys)
{
string behavior = GiveBehavior(rawCollection[key]);
rawCollection[key] = behavior;
}
This is how I update the dictionary..
Is there anyway tat can be done via LINQ...
You could try the following:
List<string> keys = rawCollection.Keys.ToList();
keys.ForEach(key => { rawCollection[key] = GiveBehavior(rawCollection[key]); });
That should do it
rawCollection = rawCollection.ToDictionary(item => item.Key, item => GiveBehavior(item.Key));