Convert paired lists into one-to-many lookup - c#-4.0

I have lists (of equal length) of paired strings, call them "source" and "target". One source may map to multiple targets. Can I convert this to a lookup table (mapping a source to a list of targets) using LINQ? One (long-winded) way of doing this is:
// There may be multiple targets corresponding to each source
// as in the following example lists.
// NB. Lists are guaranteed same length
List<string> sourceNames = new List<string>() { "A", "B", "C", "A", "B", "C" };
List<string> targetNames = new List<string>() { "Z", "Y", "X", "W", "V", "U" };
// Use extension methods to make list of unique source names
List<string> uniqueSourceNames = sourceNames.Distinct().ToList<string>();
// For each unique source, make a list of the corresponding targets
Dictionary<string, List<string>> nameLookup = new Dictionary<string, List<string>>();
foreach (string sourceName in uniqueSourceNames)
{
List<string> targetsForSource = new List<string>();
for (int index = 0; index < sourceNames.Count; index++)
{
if (sourceNames[index] == sourceName)
targetsForSource.Add(targetNames[index]);
}
nameLookup.Add(sourceName, targetsForSource);
}
// Can now use the nameLookup dictionary to map a source name to a list of target names
// e.g. this returns "Z", "W"
List<string> targets = nameLookup["A"];
Is there a way to do this more efficiently using LINQ?

You can use GroupBy and ToDictionary:
var lookup = sourceNames
.Select((Source, i) => new { Target = targetNames.ElementAt(i), Source})
.GroupBy(x => x.Source)
.ToDictionary(g => g.Key, g => g.Select(x => x.Target));
Now every distinct source-string is mapped to an IEnumerable<string> targets.
foreach (var kv in lookup)
Console.WriteLine("{0} has destinations {1}"
, kv.Key
, string.Join(",", lookup[kv.Key]));
Edit: Here's the demo: http://ideone.com/b18H7X

You can use Zip and ToLookup:
var nameLookup = sourceNames
.Zip(targetNames, (x, y) => new { Source = x, Target = y })
.ToLookup(x => x.Source, x => x.Target);

Related

How to group csv data by specific column name row wise using Java 7 only

Format of csv is
Row1:c1,c2,c3,c4,c5
Row2:1,a,b,c,d
Row3:1,n,b,c,l
Row4:2,j,k,c,l
Row5:3,a,i,v,i
Row6:1,a,b,c,d
Row7:1,a,b,u,i
Grouping should be done on basis of column c1,c2,c3, if all these three column values are same then group it as follows:
GROUP1: row2 and row 7 are same and map should be Map(Map(String, String),Map(String, String))
i.e. one key{c1,1},{c2,a},{c3,b} (key value pair again)
with multiple values as
{c4,c},{c5,d} (key value pair )
{c4,u},{c5,i} (key value pair )
GROUP2:Row 3 does not have same value with respect to columns so the map should be
key{c1,1}{c2,n}{c3,b}
value{c4,c}{c5,l}
Row7 should be added to error map as the row is same as row2
Final Map should be Map of(group.Num,Objects)
Column name should be picked from property file as shown below.
public class app3 {
public static void main(String args[]) throws IOException {
File CSV_FILE_PATH = new File("csv.csv");
CsvMapReader mapReader = null;
try {
mapReader = new CsvMapReader(new FileReader(CSV_FILE_PATH), CsvPreference.STANDARD_PREFERENCE);
final String[] header = mapReader.getHeader(true);
final CellProcessor[] processors = getProcessors();
Map<String, Object> customerMap;
Map<Integer,Map<String, Object>> finalMap = new HashMap<Integer,Map<String, Object>>();
while( (customerMap = mapReader.read(header, processors)) != null ) {
finalMap.put(mapReader.getLineNumber(), customerMap);
}
}
}
finally {
if( mapReader != null ) {
mapReader.close();
}
}
}
private static CellProcessor[] getProcessors() throws IOException {
InputStream input = new FileInputStream("fileAttributesconfig.properties");
Properties prop= new Properties();
prop.load(input);
CellProcessor[] processors=new CellProcessor[prop.size()];
for(int i=0; i< prop.size(); i++){
processors[i]=new Optional();
}
return processors;
}
}
The have done multiple trials but group by is supported only by 8, but specifically i need to use java 7.Could someone can help me here? I need more ideas to over come this

Convert string into a dictionary

I have the following piece of string: 'username=Tomas&password=123', I've tried to split the string into 2 parts then split it again to remove the equals sign but don't know how to load that into a dictionary
Once you've split the components, you can split again inside reduce() and assign to the object. You could also do this in a for loop if you don't want to use reduce().
let str = 'username=Tomas&password=123'
let components = str.split('&') // [ 'username=Tomas', 'password=123' ]
let dict = components.reduce((obj, comp) => {
let [key, val] = comp.split('=') // ['username', 'Tomas']
obj[key] = val
return obj
}, {})
console.log(dict)

Get the list of item from list that contains other list

I have a list of string.
I want to get the list items that matches the other list.
i.e, if First List values contains the value from other string return all the values.
List<string> fileNameToMatch = new List<string> { "nsevar", "cat", "elm", "nse isin", "l6scripsv2eq" };
List<string> fileToMatch = new List<string> { "fevnsevarc", "gfcatgf","ratstts","mymatch"};
The second list contains the values the are present in first list.
So return all values.
The output should be "fevnsevarc" and "gfcatgf" in list.
Can we use some link to get the names from the fileToMatch which contain data from fileNameToMatch
This should do the trick:
var res = fileToMatch
.Where(f => fileNameToMatch.Any(fn => f.IndexOf(fn) >= 0))
.ToList();
This is nearly self-explanatory: you are looking for all files f in fileToMatch such that a file name fn exists in the fileNameToMatch where f is contained anywhere in fn as a substring.
EDIT : (in response to a comment) To get the fn along with f, add a Select, and use an anonymous type, like this:
var res = fileToMatch
.Where(f => fileNameToMatch.Any(fn => f.IndexOf(fn) >= 0))
.Select(f => new {
File = f
, Name = fileNameToMatch.First(fn => f.IndexOf(fn) >= 0)
})
.ToList();
foreach (var match in res) {
Console.WriteLine("{0} - {1}", match.File, match.Name);
}
This should work:
var result = fileToMatch.Where(x => fileNameToMatch.Any(y => x.Contains(y)));

String Operation on list

I have a list of integers say
var items = new[] { 1, 2, 3, 4 };
I would like to cast them to list of strings. The reason is i need the set to be appear like
{<- 1 -> , <-2-> ,<-3-> ,<-4-> }
Normally i create another list like
List<string> list = new List<string>();
foreach (int i in items)
{
list.Add("<-" + i + "->");
}
Is there any shortcut to achieve the same result?
You could use LINQ and more specifically a combination of the .Select() and .ToList() extension methods:
var items = new[] { 1, 2, 3, 4 };
List<string> list = items.Select(i => string.Format("<-{0}->", i)).ToList();
The .Select() extension method projects each integer element to the corresponding string representation and the .ToList() extension method casts the result to a List<string>.
Try
var lst = items.ToList().ConvertAll(x=>x.ToString()).Select(x=>"<-"+ x+"->");

Grouping and sorting ObservableCollection

I am trying to iterate through a ObservableCollection grouping, but I cannot figure out the correct syntax for the foreach loop. Any thoughts?
ObservableCollection<SaleItem> list = new ObservableCollection<SaleItem>();
list.Add(new SaleItem("Beverage", "Pepsi", 3.99M));
list.Add(new SaleItem("Beverage", "Coke", 4.29M));
list.Add(new SaleItem("Clothing", "Jeans", 29.99M));
list.Add(new SaleItem("Clothing", "Shirt", 11.99M));
var foo = list.GroupBy(c => c.ItemType).AsEnumerable();
//Works, how do I iterate over it????
decimal sales = foo.FirstOrDefault().Sum(c => c.SalePrice);
// THIS IS THROWING A INVALID CAST
foreach (IEnumerable<IGrouping<string, Object>> obj in foo)
{
string f = "a";
}

Resources