Here some complication sorting in my application.
Now i have data object is like following(called pCList):
Object[0]:
Id: 1
comp: Test
med: xyz
condition: valueObject.Condition
Object[1]:
Id: 2
comp: Test1
med: pqr
condition: valueObject.Condition
Object[2]:
Id: 3
comp: Test
med: abc
condition: valueObject.Condition
condition VO Have data like:
condition data1:
conId: 001
cond: abcds
condition data2:
conId: 001
cond: trdfd
condition data3:
conId: 001
cond: dsdsds
For normal sorting i will do as following way;
var sort:ISort = new Sort();
var sortField:ISortField = new SortField("med");
sort.fields = [sortField];
if(pCList != null)
{
pCList.sort = sort;
pCList.refresh();
}
In which pcList is sort by med.
But now, I want to sort by condition.cond
like first come which have cond value abcds then dsdsds then trdfd and so on...
I Have tried it using:
var sort:ISort = new Sort();
var sortField:ISortField = new SortField("condition.cond");
sort.fields = [sortField];
But not succeed. Any help is greatly appreciated.
ISort has a property compareFunction, that can be used for custom sorting. See example below.
var sort:ISort = new Sort();
sort.compareFunction = function(a:Object, b:Object, fields:Array = null):int {
var conditionA:String = a.Condition.cond;
var conditionB:String = b.Condition.cond;
if (conditionA < conditionB) {
return -1;
} else if (conditionA > conditionB) {
return 1;
} else {
return 0;
}
};
Related
There is a database with 200 columns, how to update data from 21 to 200, not in this way -
Set Column21 = NEW.Column21,
Column22 = NEW.Column22,
Column23 = NEW.Column23,
Column24 = NEW.Column24,
Column25 = NEW.Column25...
//You can loop through 22 to 200 and make the string or access property
let sql = "Set",i=22;
[...Array(179)].forEach((_, l) => {
let column = 'Column'+i;
let val = "some val"; //access value
sql += ` ${column} = ${val} `;
i++;
});
console.log(sql);
I have the function below where I am trying to scrape 4 websites, and then combine the results into a spreadsheet. Is there a faster way to match over a large array that isn't the INDEX/MATCH formulas. My desired output would be (obv this is an example)
MLBID | FG_ID | PA | K | K% | wOBA
12345 | 12345 | 12 | 5 | 41.7% | .300
While the code I have below works, it takes wayyyy too long reaches the 6-minute limit of Google Script. The matching that I am trying to do is with ~4000 rows. I have commented my code as much as possible.
function minors_batting_stats() {
//this is the spreadsheet where I have a list of all of the IDs -- MLB and FG
var ids = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Player List");
//this is the output sheet
var mb18vR_sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("2018 minors bat vs R");
//various URLs I am trying to scrape
var mb18vR_PA_url = 'https://www.mlb.com/prospects/stats/search?level=11&level=12&level=13&level=14&level=15&level=16&pitcher_throws=R&batter_stands=&game_date_gt=&game_date_lt=&season=2017&home_away=&draft_year=&prospect=&player_type=batter&sort_by=results&sort_order=desc&group_by=name&min_pa=&min_pitches=#results'
var mb18vR_SO_url = 'https://www.mlb.com/prospects/stats/search?pa_result=strikeout&level=11&level=12&level=13&level=14&level=15&level=16&pitcher_throws=R&batter_stands=&game_date_gt=&game_date_lt=&season=2017&home_away=&draft_year=&prospect=&player_type=batter&sort_by=results&sort_order=desc&group_by=name&min_pa=&min_pitches=#results'
var mb18vR_wOBA_url = 'https://www.mlb.com/prospects/stats/search?level=11&level=12&level=13&level=14&level=15&level=16&pitcher_throws=R&batter_stands=&game_date_gt=&game_date_lt=&season=2017&home_away=&draft_year=&prospect=&player_type=batter&sort_by=woba&sort_order=desc&group_by=name&min_pa=&min_pitches=#results'
//creating an array for each scrape
var res = [];
var res1 = [];
var res2 = [];
var res3 = [];
//getting the MLB and FG ids from the spreadsheet
var mlbids = ids.getRange(1, 11, ids.getLastRow()).getValues();
var fgids = ids.getRange(1,9, ids.getLastRow()).getValues();
//scraping SO against RHP
var content_SO = UrlFetchApp.fetch(mb18vR_SO_url).getContentText();
var e_SO = Parser.data(content_SO).from('tbody').to('</tbody>').build();
var rows_SO = Parser.data(e_SO).from('<tr class="player_row"').to('</tr>').iterate();
for (var i=0; i<rows_SO.length; i++) { //rows.length
res1[i] = [];
res1[i][0] = Parser.data(rows_SO[i]).from('/player/').to('/').build();
var SOs = Parser.data(rows_SO[i]).from('<td align="left">').to('</td>').iterate();
res1[i][1] = SOs[1];
}
//scraping wOBA against RHP
var content_wOBA = UrlFetchApp.fetch(mb18vR_wOBA_url).getContentText();
var e_wOBA = Parser.data(content_wOBA).from('tbody').to('</tbody>').build();
var rows_wOBA = Parser.data(e_wOBA).from('<tr class="player_row"').to('</tr>').iterate();
for (var i=0; i<rows_wOBA.length; i++) { //rows.length
res2[i] = [];
res2[i][0] = Parser.data(rows_wOBA[i]).from('/player/').to('/').build();
var wOBAs = Parser.data(rows_wOBA[i]).from('<td align="left">').to('</td>').iterate();
res2[i][1] = wOBAs[2];
}
//scraping PA against RHP
var content = UrlFetchApp.fetch(mb18vR_PA_url).getContentText();
var e = Parser.data(content).from('tbody').to('</tbody>').build();
var rows = Parser.data(e).from('<tr class="player_row"').to('</tr>').iterate();
for (var i=0; i<rows.length; i++) { //rows.length
res[i] = [];
res[i][0] = Parser.data(rows[i]).from('/player/').to('/').build();
res[i][1] = [];
//matching the MLB_ID with FG_ID
var mlbID = res[i][0];
for(var j = 0; j<mlbids.length;j++){
if(mlbids[j] == mlbID){
res[i][1] = fgids[j];
}
}
var PAs = Parser.data(rows[i]).from('<td align="left">').to('</td>').iterate();
res[i][2] = PAs[1];
//matching the MLB_ID from PA (res) with SO (res1)
res[i][3] = 0;
for (var w=0; w<res1.length; w++) {
if (res[i][0] == res1[w][0]) {
res[i][3] = res1[w][1];
}
}
//Calculating K%
res[i][4] = res[i][3] / res[i][2]
//matching the MLB_ID from PA (res) with wOBA (res1)
res[i][5] = 0;
for (var v=0; v<res2.length; v++) {
if (res[i][0] == res2[v][0]) {
res[i][5] = res2[v][1];
}
}
}
//pasting values
mb18vR_sheet.getRange(2, 1, res.length, res[0].length).setValues(res);
}
The issue you have is that you are forcing your script to loop through large datasets many many times for each row of compared data. A better approach is to build a lookup object, which maps between a desired unique identifier and the row of the data array you want to access:
/* Make an object from an Array[][] that has a unique identifier in one of the columns.
* #param Array[][] data The 2D array of data to index, e.g. [ [r1c1, r1c2, ...], [r2c1, r2c2, ...], ... ]
* #param Integer idColumn The column in the data array that is a unique row identifier
e.g. the column index that contains the product's serial number, in a data
array that has only a single row per unique product.
#return Object {} An object that maps between an id and a row index, such that
`object[id]` = the row index for the specific row in data that has id = id
*/
function makeKey(data, idColumn) {
if(!data || !data.length || !data[0].length)
throw new ValueError("Input data argument is not Array[][]");
// Assume the first column is the column with the unique identifier if not given by the caller.
if(idColumn === undefined)
idColumn = 0;
var key = {};
for(var r = 0, rows = data.length; r < rows; ++r) {
var id = data[r][idColumn];
if (key[id])
throw new ValueError("ID is not unique for id='" + id + "'");
key[id] = r;
}
return key;
}
Usage:
var database = someSheet.getDataRange().getValues();
var lookup = makeKey(database, 3); // here we say that the 4th column has the unique values.
var newData = /* read a 2D array from somewhere */;
for(var r = 0, rows < newData.length; r < rows; ++r) {
var id = newData[r][3];
var existingIndex = lookup[id];
if (existingIndex) {
var oldDataRow = database[existingIndex];
} else {
// No existing data.
}
}
By making a lookup object for your data arrays, you no longer have to re-search them and make comparisons, because you did the search once and stored the relationship, rather than discarding it every time. Note that the key that was made is based on a specific (and unique) property of the data. Without that relationship, this particular indexing approach won't work - but a different one will.
I am making a game that when you click on the Monster your score gets +1. But when your score goes over 1000 I would like it like this 1,000 rather than 1000. I am not sure how to do this as I have not learnt much action script. I have embed number and punctuation into the font. Here is my code so far:
var score:Number = 0;
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
Monster.addEventListener(TouchEvent.TOUCH_TAP, fl_TapHandler);
function fl_TapHandler(event:TouchEvent):void
{
score = score + 1;
Taps_txt.text = (score).toString();
}
Help will greatly appreciated.
You can do like that:
function affScore(n:Number, d:int):String {
return n.toFixed(d).replace(/(\d)(?=(\d{3})+\b)/g,'$1,');
}
trace(affScore(12345678, 0)); // 12,345,678
This may not be the most elegant approach, but I wrote a function that will return the string formatted with commas;
public function formatNum(str:String):String {
var strArray:Array = str.split("");
if (strArray.length >= 4) {
var count:uint = 0;
for (var i:uint = strArray.length; i > 0; i--) {
if (count == 3) {
strArray.splice(i, 0, ",");
count = 0;
}
count++;
}
return strArray.join("");
}
else {
return str;
}
}
I tested it on some pretty large numbers, and it seems to work just fine. There's no upper limit on the size of the number, so;
trace (formatNum("10000000000000000000"));
Will output:
10,000,000,000,000,000,000
So in your example, you could use it thusly;
Taps_txt.text = formatNum(String(score));
(This is casting the type implicitly rather than explicitly using toString();, but either method is fine. Casting just looks a little neater in function calls)
Use the NumberFormatter class:
import flash.globalization.NumberFormatter;
var nf:NumberFormatter = new NumberFormatter("en_US");
var numberString:String = nf.formatNumber(1234567.89);
trace("Formatted Number:" + numberString);
// Formatted Number:1,234,567.89
To show the score with comma, you can do like this : ( comments are inside the code )
var score:Number = 0;
var score_str:String;
var score_str_len:int;
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
Monster.addEventListener(TouchEvent.TOUCH_TAP, fl_TapHandler);
function fl_TapHandler(event:TouchEvent):void
{
score = score + 1;
score_str = score.toString();
score_str_len = score_str.length;
// here you can use score > 999 instead of score_str_len > 3
Taps_txt.text =
score_str_len > 3
// example : 1780
// score_str_len = 4
// score_str.substr(0, 4 - 3) = 1 : get thousands
// score_str.substr(4 - 3) = 780 : get the rest of our number : hundreds, tens and units
// => 1 + ',' + 780 = 1,780 : concatenate thousands + comma + (hundreds, tens and units)
? score_str.substr(0, score_str_len-3) + ',' + score_str.substr(score_str_len-3)
: score_str
;
// gives :
// score == 300 => 300
// score == 1285 => 1,285
// score == 87903 => 87,903
}
EDIT :
To support numbers greater than 999.999, you can do like this :
function fl_TapHandler(event:MouseEvent):void
{
score = score + 1;
score_str = score.toString();
Taps_txt.text = add_commas(score_str);
}
function add_commas(nb_str:String):String {
var tmp_str:String = '';
nb_str = nb_str.split('').reverse().join('');
for(var i = 0; i < nb_str.length; i++){
if(i > 0 && i % 3 == 0) tmp_str += ',';
tmp_str += nb_str.charAt(i);
}
return tmp_str.split('').reverse().join('');
/*
gives :
1234 => 1,234
12345 => 12,345
123456 => 123,456
1234567 => 1,234,567
12345678 => 12,345,678
123456789 => 123,456,789
1234567890 => 1,234,567,890
*/
}
Hope that can help you.
I have two ArrayCollection and I want to merge them into one...
arr1 =
[0] -> month = 07
tot_err = 15
[1] -> month = 08
tot_err = 16
[2] -> month = 09
tot_err = 17
arr2 =
[0] -> month = 07
tot_ok = 5
[1] -> month = 08
tot_ok = 6
[2] -> month = 09
tot_ok = 7
I would like to have this array
arr3 =
[0] -> month = 07
tot_err = 15
tot_ok = 5
[1] -> month = 08
tot_err = 16
tot_ok = 6
[2] -> month = 09
tot_err = 17
tot_ok = 7
How can I do it?
EDIT:
I did this solution:
private function mergeArrays(a:ArrayCollection, b:ArrayCollection):ArrayCollection
{
for (var i:int=0;i<a.length;i++)
for each(var item:Object in b)
{
if( a[i].month == item.month){
a[i].tot_err = item.tot_err;
}
}
return a;
}
But there is an important problem, if array2 (b) has a item.month that there isn't in the array1 (a) the value is lost...
private function mergeArrays(a:ArrayCollection, b:ArrayCollection):ArrayCollection
{
var result:ArrayCollection = new ArrayCollection();
var months:Dictionary = new Dictionary();
for (var i:int = 0; i < a.length; i++)
{
var mergedItem:Object = new Object();
mergedItem.month = a[i].month;
mergedItem.tot_ok = a[i].tot_ok;
mergedItem.tot_err = null;
for (var j:int = 0; j < b.length; j++)
{
if(a[i].month == b[j].month)
{
mergedItem.tot_err = b[j].tot_err;
}
}
month[mergedItem.month] = true;
result.addItem(mergedItem);
}
// so far we have handled all occurrences between a and b,
// now we need to handle the items from b that are left
for each (var bItem:Object in b)
{
mergedItem = new Object();
mergedItem.month = bItem.month;
mergedItem.tot_err = bItem.tot_err;
mergedItem.tot_ok = null;
if (months[mergedItem.month] == null)
{
month[mergedItem.month] = true;
result.addItem(mergedItem);
}
}
return result;
}
if( a[i].month == item.month){
a[i].tot_err = item.tot_err;
// remove the item from b here. dontknow arraycollection
// should be like b.remove(item);
}
after for loops you can check if "b" still have elements, so you can add them to "a", dont forget to give "b" array objects a default tot_ok value. And another thing if an object in "a" that doesnt have equalivant in "b" you can use this.
private function mergeArrays(a:ArrayCollection, b:ArrayCollection):ArrayCollection
{
var ex:Boolean = true;
for (var i:int=0;i<a.length;i++){
for each(var item:Object in b)
{
if( a[i].month == item.month){
a[i].tot_err = item.tot_err;
ex = true;
}else{
ex = false;
}
}
if(!ex){
// give a default value here.
a[i].tot_err = 0;
}
}
return a;
}
private function mergeArrayCollections(a:ArrayCollection, b:ArrayCollection):ArrayCollection {
var c:ArrayCollection=new ArrayCollection(b.toArray()); //clone b so as not to modify b
//This loop handles all objects common to a and b
for each(var o:Object in a) {
for (var i:int=0; i<c.length; i++) {
var p:Object=c.getItemAt(i);
if(o.month==p.month) {
//if the month is the same then add the property to a
o.tot_ok=p.tot_ok;
c.removeItemAt(i);
break;
}
}
}
//This loop adds the leftover items from c to a
for each(var q:Object in c) {
q.tot_err=-1; //add this so that all objects in a are uniform
a.addItem(q);
}
return a; //Unnecessary return, a will be modified by reference
}
if array2 (b) has a item.month that there isn't in the array1 (a) use addItem() method to add new object to array1 (a) ;
I am able to do this query just fine with the test repository which is In Memory
when I move to the sqlRepository I get this error
Unsupported overload used for query operator 'Intersect'.
I assume it is because sending the query to sql is too complicated for Linq to Sql to do when it is not dealing with the Model.Model.Talent Type. Is there some way around doing a search like this with Intersect?
thanks
public class TalentService : ITalentService
{
ITalentRepository _repository = null;
private IQueryable<Talent> BasicSearch(string searchExpression)
{
IQueryable<Talent> t;
string[] sa = searchExpression.Trim().ToLower().Replace(" ", " ").Split(' ');
t = _repository.GetTalents();
foreach (string s in sa)
{
t = t.Intersect(AddBasicSearch(s), new TalentComparer());
}
return t;
}
private IQueryable<Talent> AddBasicSearch(string s)
{
IQueryable<Talent> t2 = _repository.GetTalents()
.Where(tal => tal.EyeColor.ToString().ToLower().Contains(s)
|| tal.FirstName.ToLower().Contains(s)
|| tal.LastName.ToLower().Contains(s)
|| tal.LanguagesString.ToLower().Contains(s)
);
return t2;
}
}
public class SqlTalentRepository:ITalentRepository
{
public IQueryable<Model.Model.Talent> GetTalents()
{
var tal = from t in _db.Talents
let tLanguage = GetTalentLanguages(t.TalentID)
where t.Active == true
select new Model.Model.Talent
{
Id = t.TalentID,
FirstName = t.FirstName,
LastName = t.LastName,
TalentLanguages = new LazyList<Model.Model.TalentLanguage>(tLanguage),
LanguagesString = t.TalentLanguages.ToLanguageNameString(_LanguageRepository.GetLanguages())
};
return tal ;
}
public IQueryable<Model.Model.TalentLanguage> GetTalentLanguages(int iTalentId)
{
var q = from y in this.talentLanguageList
let Languages = _LanguageRepository.GetLanguages()
where y.TalentId == iTalentId
select new Model.Model.TalentLanguage
{
TalentLanguageId = y.TalentLanguageId,
TalentId = y.TalentId,
LanguageId = y.LanguageId,
Language = Languages.Where(x => x.LanguageId == y.LanguageId).SingleOrDefault()
};
return q.AsQueryable<Model.Model.TalentLanguage>();
}
}
public static class TalentExtensions
{
public static string ToLanguageNameString(this IEnumerable<TalentLanguage> source
, IEnumerable<Model.Model.Language> allLanguages)
{
StringBuilder sb = new StringBuilder();
const string del = ", ";
foreach (TalentLanguage te in source)
{
sb.AppendFormat("{0}{1}", allLanguages
.Where(x => x.LanguageId == te.LanguageID).SingleOrDefault().LanguageName, del);
}
string sReturn = sb.ToString();
if (sReturn.EndsWith(del))
sReturn = sReturn.Substring(0, sReturn.Length - del.Length);
return sReturn;
}
}
public class TestTalentRepository : ITalentRepository
{
IList<Talent> talentList;
public TestTalentRepository(ILanguageRepository _LanguageRepo )
{
this._LanguageRepository = _LanguageRepo;
talentList = new List<Talent>();
talentLanguageList = new List<TalentLanguage>();
for (int i = 0; i < 55; i++)
{
var t = new Talent();
t.Id = i;
t.FirstName = (i % 3 == 0) ? "Ryan" : "Joe";
t.LastName = (i % 2 == 0) ? "Simpson" : "Zimmerman";
AddLanguagesToTestTalent(i, t);
talentList.Add(t);
}
}
private void AddLanguagesToTestTalent(int i, Talent t)
{
IList<Language> Languages = _LanguageRepository.GetLanguages().ToList<Language>();
Random rLanguages = new Random();
int numLanguages = rLanguages.Next(Languages.Count - 1) + 1;
t.TalentLanguages = new LazyList<TalentLanguage>();
for (int j = 0; j < numLanguages; j++)
{
var x = new TalentLanguage();
x.TalentLanguageId = j;
x.TalentId = i;
Random random2 = new Random();
int rand = random2.Next(Languages.Count);
var y = Languages.ElementAtOrDefault(rand);
Languages.RemoveAt(rand);
x.Language = y;
x.LanguageId = y.LanguageId;
t.TalentLanguages.Add(x);
}
}
public IQueryable<Talent> GetTalents()
{
var ts = from t in this.talentList
let tLanguage = GetTalentLanguages(t.Id)
where t.Active == true
select new Model.Model.Talent
{
Id = t.Id,
FirstName = t.FirstName,
LastName = t.LastName,
TalentLanguages = new LazyList<Model.Model.TalentLanguage>(tLanguage),
LanguagesString = t.TalentLanguages.ToLanguageNameString(_LanguageRepository.GetLanguages()),
City = t.City,
};
return ts.AsQueryable<Model.Model.Talent>();
}
public IQueryable<Model.Model.TalentLanguage> GetTalentLanguages(int iTalentId)
{
var q = from y in this.talentLanguageList
let Languages = _LanguageRepository.GetLanguages()
where y.TalentId == iTalentId
select new Model.Model.TalentLanguage
{
TalentLanguageId = y.TalentLanguageId,
TalentId = y.TalentId,
LanguageId = y.LanguageId,
Language = Languages.Where(x => x.LanguageId == y.LanguageId).SingleOrDefault()
};
return q.AsQueryable<Model.Model.TalentLanguage>();
}
}
If you're trying to find entries which match all of those criteria, you just need multiple where clauses:
private static readonly char[] SplitDelimiters = " ".ToCharArray();
private IQueryable<Talent> BasicSearch(string search)
{
// Just replacing " " with " " wouldn't help with "a b"
string[] terms = search.Trim()
.ToLower()
.Split(SplitDelimiters,
StringSplitOptions.RemoveEmptyEntries);
IQueryable<Talent> query = _repository.GetTalents();
foreach (string searchTerm in terms)
{
query = AddBasicSearch(query, searchTerm);
}
return query;
}
private IQueryable<Talent> AddBasicSearch(IQueryable<Talent> query, string s)
{
return query.Where(tal =>
tal.EyeColor.ToString().ToLower().Contains(s)
|| tal.FirstName.ToLower().Contains(s)
|| tal.LastName.ToLower().Contains(s)
|| tal.LanguagesString.ToLower().Contains(s)
);
}