I have one List as below:
var paths = new List<string> {
#"rootuploaded\samplefolder\1232_234234_1.jpg",
#"rootuploaded\samplefolder\1232_2342.jpg",
#"rootuploaded\samplefolder\subfolder\1232_234234_1.jpg",
#"rootuploaded\samplefolder\subfolder\1232_2342.jpg",
#"rootuploaded\file-5.txt",
#"rootuploaded\file-67.txt",
#"rootuploaded\file-a.txt",
#"rootuploaded\file1.txt",
#"rootuploaded\file5.txt",
#"rootuploaded\filea.txt",
#"rootuploaded\text.txt",
#"rootuploaded\file_sample_a.txt",
#"rootuploaded\file2.txt",
#"rootuploaded\file_sample.txt",
#"rootuploaded\samplefolder\1232_234234_2.bmp",
};
How to print output like this:
○ Group 1
rootuploaded\samplefolder\1232_234234_1.jpg,
rootuploaded\samplefolder\1232_234234_2.bmp
○ Group 2
rootuploaded\file1.txt
rootuploaded\file2.txt
rootuploaded\file5.txt
○ Group 3
rootuploaded\file-5.txt
rootuploaded\file-67.txt
○ Group 4
rootuploaded\file_sample.txt
rootuploaded\file_sample_a.txt
○ Cannot grouped
rootuploaded\samplefolder\1232_2342.jpg
rootuploaded\file-a.txt
rootuploaded\filea.txt
rootuploaded\text.txt
Grouping files based on 6 naming conventions (with top¬down priority):
FileName.ext, FileName_anything.ext, FileName_anythingelse.ext, ...
FileName.ext, FileName-anything.ext, FileName-anythingelse.ext, ...
FileName_1.ext, FileName_2.ext, ..., FileName_N.ext (maybe not continuous)
FileName-1.ext, FileName-2.ext, ..., FileName-N.ext (maybe not continuous)
FileName 1.ext, FileName 2.ext, ..., FileName N.ext (maybe not continuous)
FileName1.ext, FileName2.ext, ..., FileNameN.ext (maybe not continuous)
I used Linq to separate:
var groups1 = paths.GroupBy(GetFileName, (key, g) => new
{
key = key,
count = g.Count(),
path = g.ToList()
}).Where(x => x.count < 5 && x.count >= 2).ToList();
public string GetFileName(string fileName)
{
var index = 0;
if (fileName.Contains("_"))
index = fileName.IndexOf("_", StringComparison.Ordinal);
else if (fileName.Contains("-"))
index = fileName.IndexOf("-", StringComparison.Ordinal);
var result = fileName.Substring(0, index);
return result;
}
Try doing this:
var groups = new []
{
new { regex = #"rootuploaded\\samplefolder\\1232_234234_\d\..{3}", grp = 1 },
new { regex = #"rootuploaded\\file\d\.txt", grp = 2 },
new { regex = #"rootuploaded\\file-\d+\.txt", grp = 3 },
new { regex = #"rootuploaded\\file_sample.*\.txt", grp = 4 },
};
var results =
from path in paths
group path by
groups
.Where(x => Regex.IsMatch(path, x.regex))
.Select(x => x.grp)
.DefaultIfEmpty(99)
.First()
into gpaths
orderby gpaths.Key
select new
{
Group = gpaths.Key,
Files = gpaths.ToArray(),
};
That gives you this:
You would just have to jig around with the regex until you get exactly what you want.
Sadly, 1. and 2. group turn this solution difficult. Cause both contain 'FileName.ext', so it has to check whole list together :(
I try to separate groupping 1. 2. and 3 - 6:
First step:
Find and remove Group 1 and 2 candidates.
It orders the list base on file path:
var orderedFilenames = pathsDistinct().OrderBy(p => p).ToList();
Than find Group 1 and 2 candidates:
var groupped = orderedFilenames.GroupBy(s => GetStarterFileName(s, orderedFilenames));
private static string GetStarterFileName(string fileNameMatcher, List<string> orderedFilenames)
{
string fileNameMatcherWOExt = Path.GetFileNameWithoutExtension(fileNameMatcher);
return orderedFilenames.FirstOrDefault(p =>
{
if (p == fileNameMatcher) return true;
string p_directory = Path.GetDirectoryName(p);
string directory = Path.GetDirectoryName(fileNameMatcher);
if (p_directory != directory) return false;
string pure = Path.GetFileNameWithoutExtension(p);
if (!fileNameMatcherWOExt.StartsWith(pure)) return false;
if (fileNameMatcherWOExt.Length <= pure.Length) return false;
char separator = fileNameMatcherWOExt[pure.Length];
if (separator != '_' && separator != '-') return false;
return true;
});
}
Step two:
After first step, you got Group 1 and 2 candidates, but all others are separated into different groups.
Collect remaining path and separete group 1 and 2:
var mergedGroupps = groupped.Where(grp => grp.Count() == 1).SelectMany(grp => grp);
var starterFileNameGroups = groupped.Where(grp => grp.Count() > 1);
Step three
Now you could find 3-6 based on regex validation:
var endWithNumbersGroups = mergedGroupps.GroupBy(s => GetEndWithNumber(s));
private static string GetEndWithNumber(string fileNameMatcher)
{
string fileNameWithoutExtesion = Path.Combine(Path.GetDirectoryName(fileNameMatcher), Path.GetFileNameWithoutExtension(fileNameMatcher));
string filename = null;
filename = CheckWithRegex(#"_(\d+)$", fileNameWithoutExtesion, 1);
if (filename != null) return filename;
filename = CheckWithRegex(#"-(\d+)$", fileNameWithoutExtesion, 1);
if (filename != null) return filename;
filename = CheckWithRegex(#" (\d+)$", fileNameWithoutExtesion, 1);
if (filename != null) return filename;
filename = CheckWithRegex(#"(\d+)$", fileNameWithoutExtesion);
if (filename != null) return filename;
return fileNameWithoutExtesion;
}
private static string CheckWithRegex(string p, string filename, int additionalCharLength = 0)
{
Regex regex = new Regex(p, RegexOptions.Compiled | RegexOptions.CultureInvariant);
Match match = regex.Match(filename);
if (match.Success)
return filename.Substring(0, filename.Length - (match.Groups[0].Length - additionalCharLength));
return null;
}
Final Step:
Collect non groupped items and merge Group 1-2 and 3-6 candidates
var nonGroupped = endWithNumbersGroups.Where(grp => grp.Count() == 1).SelectMany(grp => grp);
endWithNumbersGroups = endWithNumbersGroups.Where(grp => grp.Count() > 1);
var result = starterFileNameGroups.Concat(endWithNumbersGroups);
You could try to solve both step in one shot, but as you see groupping mechanism are different. My solution is not so beautiful, but I think it's clear... maybe :)
Related
Hello every one i have string containing Address and list of Areas from Db, i want to get the area from address string which is present in both list of areas and address string.how can i do this without looping because through looping it will take to much time. Any solution please through regular expression or any other approach.
var data = _context.OrderDetails.Where(c => c.ConsignmentId == cId)
.Select(c => new {address = c.DeliveryAddress, cityId = c.CityId}).ToList();
string EncryptedConsId = _customEncryptionDecryption.Encode(cId);
Regex re = new Regex(#"^(.*?(\bkarachi\b)[^$]*)$");
MatchCollection cityA = re.Matches(data[0].address.ToLower());
if (cityA.Count != 0)
{
var cityNameA = cityA[0].Groups[2];
if (cityNameA.Value == "karachi")
{
var areasForCityA = _context.Areas.Where(a => a.CityId == 1).ToList();
}
}
i have solve my problm by doing this,Thanks to all of You Thank you so much
var areas = _context.Areas.Where(a => a.CityId == data[0].cityId).ToList();
for (int i = 0; i < areas.Count; i++)
{
var result = data[0].address.ToLower().Contains(areas[i].Title.ToLower());
if (result)
{
AreaId = areas[i].Id;
CityId = data[0].cityId;
var order = _context.OrderDetails.Where(o => o.ConsignmentId == cId).SingleOrDefault();
order.AreaId = AreaId;
_context.SaveChanges();
break;
}
}
What I implemented with a for loop is this:
phraseSources2 = new List<PhraseSource2>();
for (int i = 0; i < phraseSources.Count; i++)
{
var ps = phraseSources[i];
if (i != phraseSources.Count - 1)
{
var psNext = phraseSources[i + 1];
if (psNext != null &&
ps.Kanji == psNext.Kanji &&
ps.Kana == psNext.Kana &&
ps.English.Length <= psNext.English.Length)
{
i++;
ps = phraseSources[i];
}
} else
{
ps = phraseSources[i];
}
phraseSources2.Add(new PhraseSource2()
{
Kanji = ps.Kanji,
Kana = ps.Kana,
Furigana = ps.Furigana,
English = ps.English,
});
}
Previously I had been using LINQ
phraseSources2 = (List<Data1.Model.PhraseSource2>)phraseSources
.Select(x => new PhraseSource2()
{
Kanji = x.Kanji,
Kana = x.Kana,
Furigana = x.Furigana,
English = x.English,
}).ToList();
I know LINQ can do a lot but can it look forward at the next row when doing a select?
If I understand your problem correcly I wouldn't "look forward" but use GroupBy instead and group by Kanji and Kana then Select the longest English as the value in the PhraseSource2 object.
Something like this:
var phraseSource2 = phraseSources
.GroupBy(x => new {Kanji = x.Kanji, Kana = x.Kana})
.Select(g => new PhraseSource2 {
Kanji = g.Key.Kanji,
Kana = g.Key.Kana,
Furigana = g.First().Furigana,
English = g.OrderByDescending(x => x.English.Length).First().English
});
If the source collection can be accessed by index than you can use an overload to the select which gives you the current index.
var source = new[] { 'a', 'b', 'c' };
var result = source.Select((x, i) => new { Current = x, Next = source.Length > i+1 ? source[i+1] : ' '});
All you have to do is just set up a variable inside a query where you can easily retrieve next or previous value like this:
phraseSources2 = (List<Data1.Model.PhraseSource2>)phraseSources
.Select((x, y) =>
var NextKanji = (List<Data1.Model.PhraseSource2>)phraseSources.Skip(y + 1).FirstOrDefault().Kanji;
new PhraseSource2()
{
Kanji = NextKanji,
Kana = x.Kana,
Furigana = x.Furigana,
English = x.English,
}).ToList();
If you want to check some conditions before, you can do it like this:
phraseSources2 = (List<Data1.Model.PhraseSource2>)phraseSources
.Where((x, y) =>
var NextEnglish = (List<Data1.Model.PhraseSource2>)phraseSources.Skip(y + 1).FirstOrDefault().English;
x.English.Length < NextEnglish.Length)
.Select(x =>
new PhraseSource2()
{
Kanji = x.Kanji,
Kana = x.Kana,
Furigana = x.Furigana,
English = x.English,
}).ToList();
There is no built-in method, but there are third-party libraries that offer this functionality. The MoreLinq is a respected and free .NET library that offers a WindowLeft extension method, that processes a sequence into a series of subsequences representing a windowed subset of the original. So you could use it to process your phraseSources in pairs, and discard the pairs that have two equal phrases. Finally select the first phrase of the pairs that survived.
using static MoreLinq.Extensions.WindowLeftExtension;
var phraseSources2 = phraseSources
.WindowLeft(size: 2)
.Where(phrases => // phrases is of type IList<PhraseSource2>
{
if (phrases.Count == 2) // All have size 2 except from the last
{
var ps = phrases[0];
var psNext = phrases[1];
return ps.Kanji != psNext.Kanji || ps.Kana != psNext.Kana ||
ps.English.Length > psNext.English.Length;
}
else // The last is a single phrase
{
return true;
}
})
.Select(window => window[0]) // Select the first phrase
.ToList();
I have file names with version numbers embedded, similar to NuGet's naming scheme. Examples:
A.B.C.1.2.3.4.zip
A.B.C.1.2.3.5.zip
A.B.C.3.4.5.dll
A.B.C.1.2.3.6.zip
A.B.C.1.2.3.dll
X.Y.Z.7.8.9.0.zip
X.Y.Z.7.8.9.1.zip
Given a pattern "A.B.C.1.2.3", how do I find all those files and directories that match, regardless of version number? I support both major.minor.build.revision and major.minor.build schemes.
That is, given "A.B.C.1.2.3", return the following list:
A.B.C.1.2.3.4.zip
A.B.C.1.2.3.5.zip
A.B.C.1.2.3.6.zip
A.B.C.1.2.3.dll
A.B.C.3.4.5.dll
Bonus points for determining which file name has the highest version.
If you know the filenames end with the version, you could Split the filename string on .. Then iterate backwards from the end (skipping the extension) and stop on the first non-numeric string. (TryParse is probably good for this.) Then you can string.Join the remaining parts and you have the package name.
Do this for the search term to find the package name, then each file in the directory, and you can compare just the package names.
Credits to jdwweng for his answer as well as 31eee384 for his thoughts. This answer basically combines both ideas.
First, you can create a custom class like so:
class CustomFile
{
public string FileName { get; private set; }
public Version FileVersion { get; private set; }
public CustomFile(string file)
{
var split = file.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
int versionIndex;
int temp;
for (int i = split.Length - 2; i >= 0; i--)
{
if (!Int32.TryParse(split[i], out temp))
{
versionIndex = i+1;
break;
}
}
FileName = string.Join(".", split, 0, versionIndex);
FileVersion = Version.Parse(string.Join(".", split, versionIndex, split.Length - versionIndex - 1));
}
}
Using it to parse the filename, you can then filter based on it.
string[] input = new string[] {
"A.B.C.D.1.2.3.4.zip",
"A.B.C.1.2.3.5.zip",
"A.B.C.3.4.5.dll",
"A.B.C.1.2.3.6.zip",
"A.B.C.1.2.3.dll",
"X.Y.Z.7.8.9.0.zip",
"X.Y.Z.7.8.9.1.zip"
};
var parsed = input.Select(x => new CustomFile(x));
var results = parsed
.Where(cf => cf.FileName == "A.B.C")
.OrderByDescending(cf=>cf.FileVersion)
.ToList();
In this example, the first element would have the highest version.
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string[] input = new string[] {
"A.B.C.1.2.3.4.zip",
"A.B.C.1.2.3.5.zip",
"A.B.C.3.4.5.dll",
"A.B.C.1.2.3.6.zip",
"A.B.C.1.2.3.dll",
"X.Y.Z.7.8.9.0.zip",
"X.Y.Z.7.8.9.1.zip"
};
var parsed = input.Select(x => x.Split(new char[] { '.' }))
.Select(y => new
{
name = string.Join(".", new string[] { y[0], y[1], y[2] }),
ext = y[y.Count() - 1],
major = int.Parse(y[3]),
minor = int.Parse(y[4]),
build = int.Parse(y[5]),
revision = y.Count() == 7 ? (int?)null : int.Parse(y[6])
}).ToList();
var results = parsed.Where(x => (x.major >= 1) && (x.major <= 3)).ToList();
var dict = parsed.GroupBy(x => x.name, y => y)
.ToDictionary(x => x.Key, y => y.ToList());
var abc = dict["A.B.C"];
}
}
}
you can use new Version() to compare versions like this:
List<string> fileNames = new List<string>();
fileNnames.AddRange(new[] {
"A.B.C.1.2.3.4.zip",
"A.B.C.1.2.3.5.zip",
"A.B.C.3.4.5.dll",
"A.B.C.1.2.3.6.zip",
"A.B.C.1.2.3.dll",
"X.Y.Z.7.8.9.0.zip",
"X.Y.Z.7.8.9.1.zip" });
string filter = "a.b.c";
var files = fileNames
//Filter the filenames that start with your filter
.Where(f => f
.StartsWith(filter, StringComparison.InvariantCultureIgnoreCase)
)
//retrieve the version number and create a new version element to order by
.OrderBy(f =>
new Version(
f.Substring(filter.Length + 1, f.Length - filter.Length - 5)
)
);
Try to use regular expression like in example below
var firstPart = Console.ReadLine();
var names = new List<string>
{
"A.B.C.1.2.3.4.zip",
"A.B.C.1.2.3.5.zip",
"A.B.C.1.2.3.6.zip",
"A.B.C.1.2.3.dll",
"X.Y.Z.7.8.9.0.zip",
"X.Y.Z.7.8.9.1.zip"
};
var versionRegexp = new Regex("^" + firstPart + "\\.([\\d]+\\.){1}([\\d]+\\.){1}([\\d]+\\.){1}([\\d]+\\.)?[\\w\\d]+$");
foreach (var name in names)
{
if (versionRegexp.IsMatch(name))
{
Console.WriteLine(name);
foreach (Group group in versionRegexp.Match(name).Groups)
{
Console.WriteLine("Index {0}: {1}", group.Index, group.Value);
}
}
}
Console.ReadKey();
This works using only LINQ, assuming the file name itself doesn't end with a digit:
List<string> names = new List<string> { "A.B.C.1.2.3.4.zip",
"A.B.C.1.2.3.5.zip",
"A.B.C.3.4.5.dll",
"A.B.C.1.2.3.6.zip" ,
"A.B.C.1.2.3.dll",
"X.Y.Z.7.8.9.0.zip",
"X.Y.Z.7.8.9.1.zip" };
var groupedFileNames = names.GroupBy(file => new string(Path.GetFileNameWithoutExtension(file)
.Reverse()
.SkipWhile(c => Char.IsDigit(c) || c == '.')
.Reverse().ToArray()));
foreach (var g in groupedFileNames)
{
Console.WriteLine(g.Key);
foreach (var file in g)
Console.WriteLine(" " + file);
}
First of all I think you can use Version class for comparison.
I believe function below can get you versions starting with certain name.
It matches the starting name then performs a non greedy search until a dot and digit followed by 2 or 3 dot digit pair and any character after.
public static List<Version> GetLibraryVersions(List<string> files, string Name)
{
string regexPattern = String.Format(#"\A{0}(?:.*?)(?:\.)(\d+(?:\.\d+){{2,3}})(?:\.)(?:.*)\Z", Regex.Escape(Name));
Regex regex = new Regex(regexPattern);
return files.Where(f => regex.Match(f).Success).
Select(f => new Version(regex.Match(f).Groups[1].Value)).ToList();
}
I have a string list having characters with numbers. I just wanted to split the string to get the number and later I need to find the max number from that splitted number list.
Match String
abc
Example List Values
abc9
abc100
abc999
abc
Result
abc1000
I have tried the below code
string Result="";
var SF = (from site in db.SF where site.Code == "xyz" select site.Line2).FirstOrDefault(); // Here I ll get "abc"
int Count = (from Ps in db.Ps where Ps.No.StartsWith(SF) select Ps.No).ToList().Count;
if (Count != 0)
{
var PNo = (from Ps in db.Ps where Ps.No.StartsWith(SF) select Ps.No).ToList().LastOrDefault();
if (PNo != null)
{
int Val = Convert.ToInt32(PNo.Replace(SF, "")) + 1; // Here I need to get `abc1000` based on the above ex. list.
Res = SF + Val.ToString();
}
}
else
{
Result = SF + "1";
}
When I execute the code, It always comes with "abc10" after It reached "abc45". Any help in providing the generic logic will be appreciated.
Thanks
Try below code :
var myStrings = new List<string>();
myStrings.Add("abc9");
myStrings.Add("abc100");
myStrings.Add("abc999");
myStrings.Add("abc");
var maxNumber = "abc" + (from myString in myStrings let value = Regex.Match(myString, #"\d+").Value select Convert.ToInt32(value == string.Empty ? "0" : Regex.Match(myString, #"\d+").Value) + 1).Concat(new[] { 0 }).Max();
Use OrderByDescending() to get the biggest number and then add +1 to result
var result = (from Ps in db.Ps
where Ps.No.StartsWith(SF)
select Ps.No)
.OrderByDescending(m => m.PS.No)
.FirstOrDefault();
How about this? I tried it and it seems to do what you are describing.
public static string testString(string[] input, string startString)
{
int max = 0;
try
{
max = input.Where(s => s.StartsWith(startString) && s.Length > startString.Length)
.Max(s => int.Parse(s.Replace(startString, string.Empty)));
}
catch
{
// no worries, this means max was "abc" without a number
}
return string.Format("{0}{1}", startString, (max + 1).ToString());
}
call it with
string test = testString(new string[] { "abc1", "abc123", "abc23", "xyz23" }, "abc");
try the below mentioned code to get the Max number from your List
var max = myList.Select(v => int.Parse(v.Substring(3))).Max();
I need to sort a List<string> by comparing the list with a string
for example:
I have a List that contains the following Items.
Kaboki
kriiki
Kale
Kutta
Kiki
Kicki
Krishna
Kseaki
The search keyword is ki I need to sort the list items using the keyword in such a way that, the strings that match in the string start have should be first and the string having the matched string in the other position have to be in the last
Here is my current code
public static List<string> GetLocations(string prefixText)
{
try
{
DataTable dtlocs = (DataTable) HttpContext.Current.Session["locations"];
var dValue = from row in dtlocs.AsEnumerable()
where row.Field<string>("Location_Name").ToLower().Contains(prefixText.ToLower())
select row.Field<string>("Location_Name");
var results = dValue.OrderBy(s => s.IndexOf(prefixText, StringComparison.Ordinal));
var str = new List<string>(results.ToList());
if (!str.Any())
str.Add("No locations found");
return str;
}
catch (Exception)
{
var str = new List<string> {"No locations found"};
return str;
}
}
Here I'm able to get the first matched values to the top but cannot sort the remaining values
and I have another issue. there is a word King Koti and i'm searhing for Ko and this word comes to first.I think this happens because, the string has two sub strings and one of the substrings start with the matched word.
and can I make the matched letters to bold ??
var res = list.OrderBy(y=> !y.StartsWith("Ki", StringComparison.OrdinalIgnoreCase))
.ThenBy(x => x)
OrderBy orders false before true:
var result = list.OrderBy(s => !s.StartsWith("ki", StringComparison.OrdinalIgnoreCase))
.ThenBy(s => !s.ToLower().Contains("ki"));
I think this should work:
list = (from str in list
let idx = str.IndexOf(keyword, StringComparison.OrdinalIgnoreCase)
let change = idx != 0 ? idx : int.MinValue
orderby change
select str).ToList();
You can use a combination of Linq's OrderBy and the IndexOf methods:
var input = ...
var search = "ki";
var results = input.Select(Value => new { Value, Index = s.IndexOf(search, StringComparison.InvariantCultureIgnoreCase) })
.Where(pair => pair.Index >= 0)
.OrderBy(pair => pair.Index)
.Select(pair => pair.Value);
Or in query syntax:
var results =
from s in input
let i = s.IndexOf(search, StringComparison.InvariantCultureIgnoreCase)
where i >= 0
orderby i
select s;