ConcurrentModificationException in Spring app o - spring

I am trying to run a method to add a score to a player and also add the winner of the match to a list of finalists
The code runs fine for adding the scores, but when I also try and add the winner to the list of finalists, I get the ConcurrentModificationException : null
I have read several posts on this which all mention that they are trying to remove items from a list, but I am not trying to remove anything in this example? Just add the winner.
I have debugged and the first cycle runs fine, but the second time it runs the loop going over the players in the players list, I get the error
public void updateSemiScores(long matchPlayId, String playerOne, String playerTwo, int p1Score, int p2Score) {
//Get the matchplay
Matchplay mp = matchPlayRepo.findMatchplayById(matchPlayId);
//Get the players from this match and set their scores and points accordingly
List<MatchPlayer> players = mp.getSemiFinalists();
//Get list of finalists
List<MatchPlayer> finalistList = mp.getFinalists();
for(MatchPlayer mps : players) {
//If the current player is player 1
if(mps.getMember().getUsername().equals(playerOne)) {
//Set their score as the score for player 1
mps.setSfScore(p1Score);
matchPlayerRepo.save(mps);
//If p1 score if highest
if(p1Score > p2Score) {
//Add the current player to the finalist list
finalistList.add(mps);
//Save the updated finalist list
mp.setFinalists(finalistList);
matchPlayRepo.save(mp);
}
}
//If the current player is player 2
if(mps.getMember().getUsername().equals(playerTwo)) {
//Set their score as the score for player 2
mps.setSfScore(p2Score);
matchPlayerRepo.save(mps);
if(p2Score > p1Score) {
//Add the current player to the finalist list
finalistList.add(mps);
//Save the updated finalist list
mp.setFinalists(finalistList);
matchPlayRepo.save(mp);
}
}
}
}
EDIT
Changing the loop method from for(MatchPlayer mps : players) to for(int i = 0; i < players.size()-1; i++) solves the problem, but I have never come across this before.

Related

Parse through rotation choices to produce class schedules

I have a Sheets project with 6 worksheets. I'm using student choices to generate a series of 4 rotations in which students can visit classes.
Form Responses 1 lists names in Column D; Columns I-N list a set of six possible rotation choices
6Rot1, 6Rot2, 6Rot3, and 6Rot4 correspond to each of the four possible rotations
mainRoster6 is a master roster produced to reflect names and rotation choices
I'm trying to parse through Form Responses 1, with the following conditions:
Start by looking at Column I and see if there are "open spots" (<47) in the corresponding column of 6Rot1. If so, add the name to that column. Otherwise, check 6Rot2 and try to perform the same operation. Etc., until all 4 rotations have been checked.
If there are no open spots in either of the 4 rotations, then check Column J and try to fit the student in the correct class's name, etc.
The intention is to assign the student's top unique choices to 4 rotations. If a student has already chosen a class for one rotation, then that class should not appear again in a different rotation.
I'm trying to solve two bugs:
If a class has already been added for a student for a rotation, he should not have the same class in a newer rotation (I tried to solve this in lines 62-67 but it didn't work). In the example sheet, for the student in row 3, once the student has been placed in "Spanish I" for rotation 2, it should skip his third choice (a repeated "Spanish I" and proceed trying to place him in "Coding" for his third rotation.
The program should assign in rotations in the order of preference (first, then second, then third, etc.). Right now, if a rotation is full for a specific class, it simply ignores that class (even if the next rotation may be available) and moves on to the next choice. In the example sheet, the first student has "coding" as third choice. Although that class is not available in the 3rd rotation, it's available in the 4th rotation. The program should try to place coding in the 4th rotation and then look at the fourth choice and try to place the student into any available rotation (in this case, rotation 3 would be available).
This is what I have currently:
function myFunction() {
var mainSheet = SpreadsheetApp.openById('1ki0Cya3IWNwdLIBe0fR1vwfz_ansmfa52NilmdFBrd0');
var firstRotation = mainSheet.getSheetByName('6Rot1');
var secondRotation = mainSheet.getSheetByName('6Rot2');
var thirdRotation = mainSheet.getSheetByName('6Rot3');
var fourthRotation = mainSheet.getSheetByName('6Rot4');
var mainRoster = mainSheet.getSheetByName('mainRoster6');
var responsesSheet = mainSheet.getSheetByName('Form Responses 1');
var destinationCell;
var column;
var columnValues;
var columnContainsStudent;
var currentSheet;
var mainRosterLastRow;
var studentName;
var compassTeacher;
var classChoice;
var rotationCounter;
var choicesCounter;
var successCounter;
var rotationOneSuccess;
var rotationTwoSuccess;
var rotationThreeSuccess;
var rotationFourSuccess;
var responsesLastRow = responsesSheet.getLastRow();
var currentRotationsArray;
// var y;
var z;
for (z = 2; z < responsesLastRow + 1; z++) {
studentName = responsesSheet.getRange(z, 4).getValues();
mainRoster.getRange(z, 2).setValue(studentName);
compassTeacher = responsesSheet.getRange(z, 6).getValues();
mainRoster.getRange(z, 1).setValue(compassTeacher);
choicesCounter = 1;
successCounter = 0;
rotationCounter = 1;
rotationOneSuccess = false;
rotationTwoSuccess = false;
rotationThreeSuccess = false;
rotationFourSuccess = false;
while (choicesCounter < 7) {
if (successCounter > 4) {
break;
}
classChoice = responsesSheet.getRange(z, 8 + choicesCounter).getValues();
//test for includes in previous choices
var currentRotationsArray = mainRoster.getRange(z, 4, 1, 4).getValues();
// for (y = 0; y < 4; y++) {
// if (currentRotationsArray[0][y] == classChoice) {
// choicesCounter++;
// break;
// }
// }
while (successCounter < 4) {
if (rotationOneSuccess == false) {
currentSheet = firstRotation;
} else if (rotationTwoSuccess == false) {
currentSheet = secondRotation;
} else if (rotationThreeSuccess == false) {
currentSheet = thirdRotation;
} else {
currentSheet = fourthRotation;
}
// Set column number and get values as an array, depending on class choice
if (classChoice == "Guitar 7") {
column = 1;
columnValues = currentSheet.getRange("A1:A").getValues();
} else if (classChoice == "Communications - Video Production") {
column = 2;
columnValues = currentSheet.getRange("B1:B").getValues();
} else if (classChoice == "Communications - Journalism") {
column = 3;
columnValues = currentSheet.getRange("C1:C").getValues();
} else if (classChoice == "Coding") {
column = 4;
columnValues = currentSheet.getRange("D1:D").getValues();
} else if (classChoice == "Art 7") {
column = 5;
columnValues = currentSheet.getRange("E1:E").getValues();
} else if (classChoice == "Latin I") {
column = 6;
columnValues = currentSheet.getRange("F1:F").getValues();
} else if (classChoice == "Spanish I") {
column = 7;
columnValues = currentSheet.getRange("G1:G").getValues();
} else if (classChoice == "French I") {
column = 8;
columnValues = currentSheet.getRange("H1:H").getValues();
} else {
column = 9;
columnValues = currentSheet.getRange("I1:I").getValues();
}
// Find column's current last row
var columnLength = columnValues.filter(String).length;
//Add the student to the class's rotation, if the student isn't already there and there's >46 people already in
if (columnLength < 48) {
for(var n in columnValues){
if(columnValues[n][0] == studentName){
columnContainsStudent = true;
rotationCounter++;
break;
}
}
if (columnContainsStudent != true) {
destinationCell = currentSheet.getRange(columnLength + 1, column);
destinationCell.setValue(studentName);
if (currentSheet == firstRotation) {
rotationOneSuccess = true;
} else if (currentSheet == secondRotation) {
rotationTwoSuccess = true;
} else if (currentSheet == thirdRotation) {
rotationThreeSuccess = true;
} else {
rotationFourSuccess = true;
}
mainRoster.getRange(z, rotationCounter + 3).setValue(classChoice);
successCounter++;
rotationCounter++;
break;
}
rotationCounter++;
} else {
break
}
rotationCounter++;
}
choicesCounter++;
}
}
}
If you look at the linked sample spreadsheet, the logic is as follows:
We're looking at Form Responses 1 row by row, with the intention of adding 4 "choices" for that person in 4 rotations (6Rot1, 6Rot2, etc.). Columns I-N list 6 possible choices, but ideally I want to put the student in his top 4 choices.
I want the program to first look at Form Responses 1 Column I. In the sample sheet it's "Guitar 7". Then the program should look at 6Rot1 and see if the column for "Guitar 7" (Column A) has any open spaces (i.e. if it has less than 48 rows already filled). In the sample sheet, there are 36 "Placeholder Names" so it adds the first student to the first blank row in Column A (row 37). Once the name has been added to one of the rotations, mainRoster6 reflects that same information (column D in this case reflects "Guitar 7", which contains the same information for 6Rot1).
The program should next look back at Form Responses 1 and consider the student's next choice: in this case it would be the "second choice" (column J, "French I"). I want to look at the first possible rotation (6Rot1, 6Rot2, etc) that doesn't include the student AND has less than 48 people already in it, so that we can put his choice there. In this case6Rot2had already 39 names in it, so it accepted the student into row 40 of column H. After adding the student name in6Rot2, the program adds "French I" to Column E ofmainRoster6` ("Rotation 2")
It next looks back at Form Responses 1 and consider the "third choice" in column K, and repeats the process.
If a class is already "full" (has more than 47 names listed already in the column), then the program should try to find the first available rotation. For example, the student in Form Responses 1 has "Coding" as his third choice. 6Rot3 already has no space for column D ("Coding"), so it should first try to add the student's name ("Billy Kid") in Column D of 6Rot4 and then try to put in the student's name in column I of 6Rot3 for "German I".
What I'm trying to do with the code is parsing row by row of Form Responses 1 and putting the first 4 possible "choices" into the 4 possible rotations, and having mainRoster6 reflect that same information as a kind of abbreviated master list.
If a student lists two or more similar choices in Form Responses 1 columns I-N, then the program should ignore the repeated values. For example, in row 3 of Form Responses 1 it should overlook the second "Spanish" in K3 and move on to the next choice for that student.
If there are no more possible choices or rotations that can account for that choice, then the space can be left blank (see for example Form Responses 1 row 4: based on the student's choices and available spots, we were only able to honor the first three choices ("French" was already filled completely in 6Rot4; "Latin I" and "Art 7" were already in the schedule).
Under these conditions, I would have expected mainRoster6 to have this intended output:

Laravel get the position/rank of a row in collection

I have a leaderboards and leaderboard_scores table.
leaderboard_scores has columns: id, leaderboard_id, player_id, score.
In Leaderboard model, I defined a hasMany relationship with LeaderboardScore:
public function scores()
{
return $this->hasMany('App\LeaderboardScore')->orderBy('score', 'desc');
}
Now I want to get the ranking of a player in a particular leaderboard.
So in controller, I do a loop to find the position given a player_id:
$score = $leaderboard->scores;
$scoreCount = $scores->count();
$myScore = $scores->where('player_id', $request->query('player_id'));
if ($myScore) { // If got my score
$myRank = 1;
for ($i = 0; $i < $scoreCount; $i++) {
if ($scores[$i]->player_id == $request->query('player_id')) {
$myRank = $i + 1;
break;
}
}
// Output or do something with $myRank
}
It works fine, but I am worried about the performance, when I have hundred thousand of players and they constantly getting their ranking. The for loop seems not a good option.
Should I use raw database query? Or any better idea?
Here some ideas
Instead of calculating ranks each time you get request, then loop through all data. you can just store players rank either in database or cache. You only need to calculate them when new player added/deleted or score changed.
Check For Caching:
https://laravel.com/docs/5.4/redis
if you still want to calculate it everytime you can use mysql rank function which is more optimized than a primitive loop
Mysql Rank : http://www.folkstalk.com/2013/03/grouped-rank-function-mysql-sql-query.html

Firebase: How to match opponents in a game?

I'm implementing a social chess game. Every user can create a new game, and they'll wait until the system will find an opponent for them.
When user creates a game, they specify constraints: color they'd like to play, and opponent's minimal chess rating.
Opponents can either match or not match. For example, the following two opponents will match:
// User 1 with rating 1700 // User 2 with rating 1800
// creates this game // creates this game
game: { game: {
color: 'white', minRating: 1650
minRating: 1600 }
} // User did not specify a preferred color,
// meaning they do not care which color to play
So, if User 1 is the first user in the system, and created their game, they'll wait. Once User 2 creates their game, they should be matched immediately with User 1.
On the other side, the following two opponents won't match, because they both want to play white. In this case, both should wait until someone else creates a game with color: 'black' (or color not specified), and minRating that would match the requirements.
// User 1 with rating 1700 // User 2 with rating 1800
// creates this game // creates this game
game: { game: {
color: 'white', color: 'white'
minRating: 1600 minRating: 1650
} }
My concerns related to scenarios where thousands of users creates new games at the same time. How do I make sure that I match opponents without creating deadlocks? i.e. how do I prevent scenarios when User 1, User 2, and User 3 are trying to find an opponent at the same time, and their matching algorithms return User 99. How do I recover from this scenario, assigning User 99 to only one of them?
How would you use the power of Firebase to implement such a matching system?
The obvious choice for a starting point would be the color, since this is an exclusive requirement. The others seem more like weighted results, so those could simply increment or decrement the weight.
Utilize priorities for min/max ranges, and keep each in a separate "index". Then grab the matches for each and create a union. Consider this structure:
/matches
/matches/colors/white/$user_id
/matches/ranking/$user_id (with a priority equal to ranking)
/matches/timezones/$user_id (with a priority of the GMT relationship)
Now to query, I would simply grab the matches in each category and rank them by the number of matches. I can start with colors, because this presumably isn't an optional or relative rating:
var rootRef = new Firebase('.../matches');
var VALUE = {
"rank": 10, "timezone": 5, "color": 0
}
var matches = []; // a list of ids sorted by weight
var weights = {}; // an index of ids to weights
var colorRef = rootRef.child('colors/black');
colorRef.on('child_added', addMatch);
colorRef.child('colors/black').on('child_removed', removeMatch);
var rankRef = rootRef.child('ranking').startAt(minRank).endAt(maxRank);
rankRef.on('child_added', addWeight.bind(null, VALUE['rank']));
rankRef.on('child_removed', removeWeight.bind(null, VALUE['rank']));
var tzRef = ref.child('timezone').startAt(minTz).endAt(maxTz);
tzRef.on('child_added', addWeight.bind(null, VALUE['timezone']));
tzRef.on('child_removed', removeWeight.bind(null, VALUE['timezone']));
function addMatch(snap) {
var key = snap.name();
weights[key] = VALUE['color'];
matches.push(key);
matches.sort(sortcmp);
}
function removeMatch(snap) {
var key = snap.name();
var i = matches.indexOf(key);
if( i > -1 ) { matches.splice(i, 1); }
delete weights[key];
}
function addWeight(amt, snap) {
var key = snap.name();
if( weights.hasOwnProperty(key) ) {
weights[key] += amt;
matches.sort(sortcmp);
}
}
function removeWeight(amt, snap) {
var key = snap.name();
if( weights.hasOwnProperty(key) ) {
weights[key] -= amt;
matches.sort(sortcmp);
}
}
function sortcmp(a,b) {
var x = weights[a];
var y = weights[b];
if( x === y ) { return 0; }
return x > y? 1 : -1;
}
Okay, now I've given what everyone asks for in this use case--how to create a rudimentary where clause. However, the appropriate answer here is that searches should be performed by a search engine. This is no simple where condition. This is a weighted search for the best matches, because fields like color are not optional or simply the best match, while others--ranking maybe--are the closest match in either direction, while some simply affect the quality of the match.
Check out flashlight for a simple ElasticSearch integration. With this approach, you should be able to take advantage of ES's great weighting tools, dynamic sorting, and everything else you need to conduct a proper matching algorithm.
Regarding deadlocks. I would not put too much focus here until you have hundreds of transactions per second (i.e. hundreds of thousands of users competing for matches). Split out the path where we will write to accept a join and do a transaction to ensure only one person succeeds in obtaining it. Keep it separate from the read data so that the lock on that path won't slow down processing. Keep the transaction to a minimal size (a single field if possible).
It is a challenging task in NoSQL environment especially if you want to match multiple fields
in your case, I would setup a simple index by color and within the color I would store the reference to the game with priority set to minRating.
That way you can query the games by the prefered colour with the priority of minRating.
indexes: {
color:{
white:{
REF_WITH_PRIORITY_TO_RATING: true
},
black:{
REF_WITH_PRIORITY_TO_RATING: true
}
}
}
if you want to get info whenever the match opens the game:
ref = new(Firebase)('URL');
query =ref.child('color_index/white/').startAt(minPriority);
query.on('child_added',function(snapshot){
//here is your new game matching the filter
});
This, however, it would get more complex if you introduce multiple fields for filtering the games for example dropRate, timeZone, 'gamesPlayed' etc... In this case, you can nest the indexes deeper:
indexes: {
GMT0: {
color:{
white:{
REF_WITH_PRIORITY_TO_RATING: true
},
black:{
REF_WITH_PRIORITY_TO_RATING: true
},
}
GMT1: {
// etc
}
}

Getting top 100 URL from a log file

One of my friends was asked the following question in an interview. Can anyone tell me how to solve it?
We have a fairly large log file, about 5GB. Each line of the log file contains an url which a user has visited on our site. We want to figure out what's the most popular 100 urls visited by our users. How to do it?
In case we have more than 10GB RAM, just do it straight forward with hashmap.
Otherwise, separate it into several files, using a hash function. And then process each file and get a top 5. With "top 5"s for each file, it will be easy to get an overall top 5.
Another solution can be sort it using any external sorting method. And then scan the file once to count each occurrence. In the process, you don't have to keep track of the counts. You can safely throw anything that doesn't make into top5 away.
Just sort the log file according to the URLs (needs constant space if you chose an algorithm like heap sort or quick sort) and then count for each URL how many times it appears (easy, the lines with the same URLs are next to each other).
Overall complexity is O(n*Log(n)).
Why splitting in many files and keeping only top 3 (or top 5 or top N) for each file is wrong:
File1 File2 File3
url1 5 0 5
url2 0 5 5
url3 5 5 0
url4 5 0 0
url5 0 5 0
url6 0 0 5
url7 4 4 4
url7 never makes it to the top 3 in the individual files but is the best overall.
Because the log file is fairly large you should read the log-file using a stream-reader. Don't read it all in the memory.
I would expect it is feasible to have the number of possible distinct links in the memory while we work on the log-file.
// Pseudo
Hashmap map<url,count>
while(log file has nextline){
url = nextline in logfile
add url to map and update count
}
List list
foreach(m in map){
add m to list
}
sort the list by count value
take top n from the list
The runtime is O(n) + O(m*log(m)) where n is the size of the log-file in lines and where the m is number of distinct found links.
Here's a C# implementation of the pseudo-code. An actual file-reader and a log-file is not provided.
A simple emulation of reading a log-file using a list in the memory is provided instead.
The algorithm uses a hashmap to store the found links. A sorting algorithm founds the top 100 links afterward. A simple data container data-structure is used for the sorting algorithm.
The memory complexity is dependent on expected distinct links.
The hashmap must be able to contain the found distinct links,
else this algorithm won't work.
// Implementation
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main(string[] args)
{
RunLinkCount();
Console.WriteLine("press a key to exit");
Console.ReadKey();
}
class LinkData : IComparable
{
public string Url { get; set; }
public int Count { get; set; }
public int CompareTo(object obj)
{
var other = obj as LinkData;
int i = other == null ? 0 : other.Count;
return i.CompareTo(this.Count);
}
}
static void RunLinkCount()
{
// Data setup
var urls = new List<string>();
var rand = new Random();
const int loglength = 500000;
// Emulate the log-file
for (int i = 0; i < loglength; i++)
{
urls.Add(string.Format("http://{0}.com", rand.Next(1000)
.ToString("x")));
}
// Hashmap memory must be allocated
// to contain distinct number of urls
var lookup = new Dictionary<string, int>();
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
// Algo-time
// O(n) where n is log line count
foreach (var url in urls) // Emulate stream reader, readline
{
if (lookup.ContainsKey(url))
{
int i = lookup[url];
lookup[url] = i + 1;
}
else
{
lookup.Add(url, 1);
}
}
// O(m) where m is number of distinct urls
var list = lookup.Select(i => new LinkData
{ Url = i.Key, Count = i.Value }).ToList();
// O(mlogm)
list.Sort();
// O(m)
var top = list.Take(100).ToList(); // top urls
stopwatch.Stop();
// End Algo-time
// Show result
// O(1)
foreach (var i in top)
{
Console.WriteLine("Url: {0}, Count: {1}", i.Url, i.Count);
}
Console.WriteLine(string.Format("Time elapsed msec: {0}",
stopwatch.ElapsedMilliseconds));
}
}
Edit: This answer has been updated based on the comments
added: running time and memory complexity analysis
added: pseudo-code
added: explain how we manage a fairly large log-file

How can I sort a secondary column in dataGridView?

I have a datagridview that I am looking to sort. One column is headed "Page" which is a number 2-3 digits long. I have another column called "Item" which is a number between 1 and 3. I am trying to sort the data by both, page first and then item:
(example)
Page 01, item 1
Page 01, item 2
Page 02, item 2
Page 02, item 3
I know this post is a little old but I came here looking for answers and no one else had responded, so I thought i'd put one up here.
First thing you need to do is disable the automatic sort on the header. After I bind the datasource to the dgv i call a disableSort method, which I actually found works best by setting sort mode to programmatic
private void disableSort()
{
foreach(DataGridViewColumn dc in dataGridView2.Columns)
{
dc.SortMode = DataGridViewColumnSortMode.Programmatic;
}
}
Then i tied into the CellMouseDown event on the dataGrid
private void dataGridView2_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex == -1)
{
int rIdx = dataGridView2.SelectedCells[0].RowIndex;
seq = dataGridView2.Rows[rIdx].Cells["SequenceNo"].Value.ToString();
int selectedColumnIdx = e.ColumnIndex;
ListSortDirection direction;
if (dataGridView2.SortedColumn == dataGridView2.Columns[selectedColumnIdx])
{
if (dataGridView2.SortOrder == SortOrder.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
direction = ListSortDirection.Ascending;
}
}
else
{
direction = ListSortDirection.Ascending;
}
this.dataGridView2.Sort(this.dataGridView2.Columns["SequenceNo"], ListSortDirection.Ascending);
this.dataGridView2.Sort(this.dataGridView2.Columns[selectedColumnIdx], direction);
}
if you go in through the ColumnHeaderMouseClick event you can skip my RowIndex check
I'm using SequenceNumber as my secondary sort (after the user selects a column to sort, they want it to be sorted by sequenceNumber). Just figure out which column the user is sorting, what its current sortorder is, take the opposite, then sort your secondary column first, then your primary.

Resources