how to sort mapreduce results? - c#

I have written a method in C# which retrieves tweets from mongoDB and would like to count and sort authors by the number of retweets.
Right now, the method already performs map and reduce and returns unsorted results the following way:
public void RetweetsCount()
{
string wordMap = #"function wordMap() {
var usernameOrigin = this.text.match(/\brt\s*#(\w+)/i);
if (usernameOrigin === null) {
return;
}
// loop every word in the document
emit(usernameOrigin[1], { count : 1 });
}";
string wordReduce = #"function wordReduce(key, values) {
var total = 0;
for (var i = 0; i < values.length; i++) {
total += values[i].count;
}
return { count : total };
}";
var options = new MapReduceOptionsBuilder();
options.SetOutput(MapReduceOutput.Inline);
var results = collection.MapReduce(wordMap, wordReduce, options);
foreach (var result in results.GetResults())
{
Console.WriteLine(result.ToJson());
}
}
Does anyone know how sort results by descending count value (number of retweets)?

Here's the solution. After retrieving results from MapReduce, I first converted the IEnumerable to list and then ordered the list the folliwing way:
var results = collection.MapReduce(wordMap, wordReduce, options);
IEnumerable<BsonDocument> resultList = results.GetResults();
List<BsonDocument> orderedList = resultList.ToList().OrderByDescending(x => x[1]).ToList();

Related

How to get a specific set of keys from Redis

I have a list of keys that I want to get from Redis. I wrote a function like this but it returns everything:
public IOrderedEnumerable<Fields> GetValues(List<string> symbols)
{
var retVal = new List<Fields>();
var patternStr = "[";
int count = 0;
foreach (var symbol in symbols)
{
patternStr += (symbol);
if (++count != symbols.Count)
{
patternStr += ", ";
}
}
patternStr += "]*";
foreach (var ep in redis.GetEndPoints())
{
var server = redis.GetServer(ep);
var keysList = server.Keys(database: 0, pattern: patternStr).ToList();
var keys = keysList.ToArray();
Console.WriteLine("Number of Symbols in this range{0} ", keys.Length);
foreach (var rk in keys)
{
var myValTask = db.StringGetAsync(rk.ToString());
var myVal = myValTask.Result;
var jsonStr = myVal.ToString();
...
}
...
}
...
The section of code I believe is the problem. I just want Redis to return the subset of keys, and I am building a pattern by seperating them by "," :
var patternStr = "[";
int count = 0;
foreach (var symbol in symbols)
{
patternStr += (symbol);
if (++count != symbols.Count)
{
patternStr += ", ";
}
}
patternStr += "]*";
I know I can get all the keys, then filter them once I get them, but I want to avoid the network thrashing...
Edit 1
BTW, the keys look like this:
127.0.0.1:6379> keys *
1) "BBWI_2022-08-19"
2) "ABBV_2023-01-20"
3) "ZTS_2022-10-21"
You can use Scan command on redis server like this:
public async Task<List<string>> ScanKeysAsync(string match, string count)
{
var schemas=new List<string>();
int nextCursor = 0;
do
{
RedisResult redisResult =await _redisServer.ExecuteAsync("SCAN", nextCursor.ToString(), "MATCH", match, "COUNT", count);
var innerResult = (RedisResult[])redisResult;
nextCursor = int.Parse((string)innerResult[0]);
List<string> resultLines = ((string[])innerResult[1]).ToList();
schemas.AddRange(resultLines);
}
while (nextCursor != 0);
return schemas;
}
and in your case would be something like this:
var keys=await ScanKeysAsync("BBWI*",10);//return max 10 occurance of pattern
But I recommend to use Scan in very special scenarios because it acts as a cursor and will iterate between all keys in redis to find match and also please read this https://redis.io/commands/scan
Test
var redis = scope.ServiceProvider.GetRequiredService<IDatabase>();
await redis.StringSetAsync("BBWI_2022-08-19", "test1",TimeSpan.FromMinutes(5));
await redis.StringSetAsync("BBWI_20fd22-08-19", "test2", TimeSpan.FromMinutes(5));
await redis.StringSetAsync("ABBV_2023-08-19", "test3", TimeSpan.FromMinutes(5));
var foundKeys = await ScanKeysAsync("BBW*", "10");
//BBWI_2022-08-19
//BBWI_20fd22-08-19
If I understand correctly, you have a set of discrete keys that you want to fetch in a single batch. If so, you can fetch all items (within reason, say < 1000 at a time) by passing a RedisKey[] array to StringGetAsync:
var keys = symbols.Select(symbol => (RedisKey)symbol).ToArray();
var values = await db.StringGetAsync(keys);
// .. use values

I want to show a list item's index in a loop

List<string> lst = new List<string>() { "mahdi","arshia","amir"};
int a = 0;
var list_mian = lst[a];
for (int i = a; i <Convert.ToInt16(list_mian); i++) //Additional information: Input string was not in a correct format.
{
MessageBox.Show(lst.IndexOf(lst[0]).ToString());
}
I want to show a list item's index in a loop, for example of mahdi's index is 0 and amir's index is 2 i wanna show their index respectively in a "for" loop and i give an error that i show that in the code part
Your trying to convert an integer to a string and then use that as a range on the for loop just use .count and compare it to the name attached to that index of the list. Hope you find this useful.
public static int? findPerson(string name)
{
List<string> lst = new List<string>() { "mahdi", "arshia", "amir" };
int? result = null;
for (int i = 0; i < lst.Count; i++) //Additional information: Input string was not in a correct format.
{
if (lst[i] == name)
{
result = i;
}
}
return result;
}
static void Main(string[] args)
{
var index = findPerson("arshia");
if (index == null)
{
Console.WriteLine("PersonNotFound");
}
else {
Console.WriteLine("Index of " + index.ToString());
}
}
You can do it with IndexOf it returns the index or -1 when there is no item.
List<string> list = new List<string>() { "mahdi", "arshia", "amir" };
var indexOfAmir = list.IndexOf("amir"); // 2
var indexOfMax = list.IndexOf("max"); // -1

Find values which sum to 0 in Excel with many items

I have to find each subset in a enough big list, 500/1000 items that are positive and negative and are decimal, whiches sum to 0. I'm not an expert so I read many and many articles and solutions, and then I wrote my code. Datas comes from Excel worksheet and I would to mark found sums there.
Code works in this way:
Initally I find all pair that sum to 0
Then I put the remains sums into a list and take the combinations within 20 items, beacause I know the it is not possible bigger combination sum to 0
In these combinations I search if one combinations sums to 0 and save it in result list, else save sum in dictionary as key and then I'll search if dictionary contains next sums (so I check pairs of these subsets)
I keep track of the index so I can reach and modify the cells
To found solutions is enough fast but when I want elaborate the results in Excel become really slow. I don't take care about find all solutions but I want to find as max as possible in a short time.
What do you think about this solution? How can I improve the speed? How can I skip easly the sums that are already taken? And how can mark the cells fastly in my worksheet, beacuse now here is the bottleneck of the program?
I hope it is enough clear :) Thanks to everybody for any help
Here my code of the combination's part:
List<decimal> listDecimal = new List<decimal>();
List<string> listRange = new List<string>();
List<decimal> resDecimal = new List<decimal>();
List<IEnumerable<decimal>> resDecimal2 = new List<IEnumerable<decimal>>();
List<IEnumerable<string>> resIndex = new List<IEnumerable<string>>();
Dictionary<decimal, int> dicSumma = new Dictionary<decimal, int>();
foreach (TarkistaSummat.CellsRemain el in list)
{
decimal sumDec = Convert.ToDecimal(el.Summa.Value);
listDecimal.Add(sumDec);
string row = el.Summa.Cells.Row.ToString();
string col = el.Summa.Cells.Column.ToString();
string range = el.Summa.Cells.Row.ToString() + ":" + el.Summa.Cells.Column.ToString();
listRange.Add(range);
}
var subsets = new List<IEnumerable<decimal>> { new List<decimal>() };
var subsetsIndex = new List<IEnumerable<string>> { new List<string>() };
for (int i = 0; i < list.Count; i++)
{
if (i > 20)
{
List<IEnumerable<decimal>> parSubsets = subsets.GetRange(i, i + 20);
List<IEnumerable<string>> parSubsetsIndex = subsetsIndex.GetRange(i, i + 20);
var Z = parSubsets.Select(x => x.Concat(new[] { listDecimal[i] }));
//var Zfound = Z.Select(x => x).Where(w => w.Sum() ==0);
subsets.AddRange(Z.ToList());
var Zr = parSubsetsIndex.Select(x => x.Concat(new[] { listRange[i] }));
subsetsIndex.AddRange(Zr.ToList());
}
else
{
var T = subsets.Select(y => y.Concat(new[] { listDecimal[i] }));
//var Tfound = T.Select(x => x).Where(w => w.Sum() == 0);
//resDecimal2.AddRange(Tfound);
//var TnotFound = T.Except(Tfound);
subsets.AddRange(T.ToList());
var Tr = subsetsIndex.Select(y => y.Concat(new[] { listRange[i] }));
subsetsIndex.AddRange(Tr.ToList());
}
for (int i = 0; i < subsets.Count; i++)
{
decimal sumDec = subsets[i].Sum();
if (sumDec == 0m)
{
resDecimal2.Add(subsets[i]);
resIndex.Add(subsetsIndex[i]);
continue;
}
else
{
if(dicSumma.ContainsKey(sumDec * -1))
{
dicSumma.TryGetValue(sumDec * -1, out int index);
IEnumerable<decimal> addComb = subsets[i].Union(subsets[index]);
resDecimal2.Add(addComb);
var indexComb = subsetsIndex[i].Union(subsetsIndex[index]);
resIndex.Add(indexComb);
}
else
{
if(!dicSumma.ContainsKey(sumDec))
{
dicSumma.Add(sumDec, i);
}
}
}
}
for (int i = 0; i < resIndex.Count; i++)
{
//List<Range> ranges = new List<Range>();
foreach(string el in resIndex[i])
{
string[] split = el.Split(':');
Range cell = actSheet.Cells[Convert.ToInt32(split[0]), Convert.ToInt32(split[1])];
cell.Interior.ColorIndex = 6;
}
}
}

Sort a List in which each element contains 2 Values

I have a text file that contains Values in this Format: Time|ID:
180|1
60 |2
120|3
Now I want to sort them by Time. The Output also should be:
60 |2
120|3
180|1
How can I solve this problem? With this:
var path = #"C:\Users\admin\Desktop\test.txt";
List<string> list = File.ReadAllLines(path).ToList();
list.Sort();
for (var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
I got no success ...
3 steps are necessary to do the job:
1) split by the separator
2) convert to int because in a string comparison a 6 comes after a 1 or 10
3) use OrderBy to sort your collection
Here is a linq solution in one line doing all 3 steps:
list = list.OrderBy(x => Convert.ToInt32(x.Split('|')[0])).ToList();
Explanation
x => lambda expression, x denotes a single element in your list
x.Split('|')[0] splits each string and takes only the first part of it (time)
Convert.ToInt32(.. converts the time into a number so that the ordering will be done in the way you desire
list.OrderBy( sorts your collection
EDIT:
Just to understand why you got the result in the first place here is an example of comparison of numbers in string representation using the CompareTo method:
int res = "6".CompareTo("10");
res will have the value of 1 (meaning that 6 is larger than 10 or 6 follows 10)
According to the documentation->remarks:
The CompareTo method was designed primarily for use in sorting or alphabetizing operations.
You should parse each line of the file content and get values as numbers.
string[] lines = File.ReadAllLines("path");
// ID, time
var dict = new Dictionary<int, int>();
// Processing each line of the file content
foreach (var line in lines)
{
string[] splitted = line.Split('|');
int time = Convert.ToInt32(splitted[0]);
int ID = Convert.ToInt32(splitted[1]);
// Key = ID, Value = Time
dict.Add(ID, time);
}
var orderedListByID = dict.OrderBy(x => x.Key).ToList();
var orderedListByTime = dict.OrderBy(x => x.Value).ToList();
Note that I use your ID reference as Key of dictionary assuming that ID should be unique.
Short code version
// Key = ID Value = Time
var orderedListByID = lines.Select(x => x.Split('|')).ToDictionary(x => Convert.ToInt32(x[1]), x => Convert.ToInt32(x[0])).OrderBy(x => x.Key).ToList();
var orderedListByTime = lines.Select(x => x.Split('|')).ToDictionary(x => Convert.ToInt32(x[1]), x => Convert.ToInt32(x[0])).OrderBy(x => x.Value).ToList();
You need to convert them to numbers first. Sorting by string won't give you meaningful results.
times = list.Select(l => l.Split('|')[0]).Select(Int32.Parse);
ids = list.Select(l => l.Split('|')[1]).Select(Int32.Parse);
pairs = times.Zip(ids, (t, id) => new{Time = t, Id = id})
.OrderBy(x => x.Time)
.ToList();
Thank you all, this is my Solution:
var path = #"C:\Users\admin\Desktop\test.txt";
List<string> list = File.ReadAllLines(path).ToList();
list = list.OrderBy(x => Convert.ToInt32(x.Split('|')[0])).ToList();
for(var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestClass {
public static void main(String[] args) {
List <LineItem> myList = new ArrayList<LineItem>();
myList.add(LineItem.getLineItem(500, 30));
myList.add(LineItem.getLineItem(300, 20));
myList.add(LineItem.getLineItem(900, 100));
System.out.println(myList);
Collections.sort(myList);
System.out.println("list after sort");
System.out.println(myList);
}
}
class LineItem implements Comparable<LineItem>{
int time;
int id ;
#Override
public String toString() {
return ""+ time + "|"+ id + " ";
}
#Override
public int compareTo(LineItem o) {
return this.time-o.time;
}
public static LineItem getLineItem( int time, int id ){
LineItem l = new LineItem();
l.time=time;
l.id=id;
return l;
}
}

C# Mongo Query In not returning result

I'm having issue with getting back a result when using the Mongo In query. When I've tested the same query in native mongo, it's bringing back the correct result. Im trying to bring back all the ids that match in the temp array.
var temp = new BsonValue [collection.Count()];
for (int i = 0; i < collection.Count(); i++)
{
temp[i] = collection[i].ID;
}
var query = Query.In("ID", temp);
var collection2 = db.GetCollection<TXT>("TXT").Find(query).ToList();
What version of MongoDb's C# driver are you using? It looks like you might be using a deprecated version of the driver.
Here is an example of how we use to use the .In filter in version 2.0.1.27:
var filter = Builders<INVENTTXT>.Filter.In(item => item.ITEMID, temp);
var result = await db.GetCollection<INVENTTXT>("INVENTTXT")
.Find(filter)
.ToListAsync()
.Result;
In the legacy driver, assuming that your INVENTTXT looks something like this:
class INVENTTXT
{
[BsonId]
public ObjectId _id { get; set; }
public String ITEMID { get; set; }
}
Then this works for me to pull the values back:
public static void GetWhereIn()
{
var collection = new List<INVENTTXT>()
{
new INVENTTXT {ITEMID = "52719635"}
};
var temp = new BsonValue[collection.Count()];
for (int i = 0; i < collection.Count(); i++)
{
temp[i] = collection[i].ITEMID;
}
var query = Query.In("ITEMID", collection.Select(c => BsonValue.Create(c.ITEMID)));
var collection2 = db.GetCollection<INVENTTXT>("INVENTTXT").Find(query).ToList();
var count = collection2.Count;
}

Categories