JSLINQ - Distinct from multiple columns - distinct

I have a working JSFiddle demo
https://jsfiddle.net/u1fohjxw/
The idea is to create a unique list of items, based on multiple fields.
I know the way I've implemented it could be improved, but need advice on doing it in JSLINQ.
This filtered unique list I then manually loop and add again - this could be done in JSLINQ.
Please indicate how this should be done :
var myList = [
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Sutherland"},
{FirstName:"John",LastName:"Ronald"},
{FirstName:"Steve",LastName:"Pinkerton"}
];
var exampleArray = JSLINQ(myList)
.Distinct(function(item){ return item.FirstName.concat(";",item.LastName)}).items
var newList = [];
for (var x = 0 ; x < exampleArray.length ; x++) {
var arraylist = exampleArray[x].split(";");
var y= new Object();
y.FirstName = arraylist[0];
y.LastName = arraylist[1];
newList.push(y);
};

how you doing? :)
Maybe something like this helps you out:
var myList = [
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Sutherland"},
{FirstName:"John",LastName:"Ronald"},
{FirstName:"Steve",LastName:"Pinkerton"}
];
var resultList = myList.Distinct(function(x){
return {
FirstName: x.FirstName,
LastName: x.LastName
}
}).ToArray();
This will return an array of the object returned inside the distinct.
Edit:
Change the distinct method to this:
Distinct: function(clause) {
var item, dict = {}, retVal = [];
for (var i = 0; i < this.items.length; i++) {
item = clause.apply(this.items[i], [this.items[i]]);
if (dict[JSON.stringify(item)] === undefined) {
dict[JSON.stringify(item)] = true;
retVal.push(item);
}
}
dict = null;
return JSLINQ(retVal);
},
It's not stress tested, I don't know how much time will take to iterate through 10k+ objects, but it's something to study and improve! :)
There's another possible fix to this if you want to try.
Cheers!

Related

Google Apps Script to add links to google Spreadsheet adds 5000+ rows and i dont understand why?

Pretty self explanatory title, im trying to do a script where it takes the number on a column and uses that to search a list of files from google drive and then insert a link to that file.
HOWEVER, every time it inserts that link it also creates like 5000+ empty rows? why would this be?
The purpose is to take a number from a certain column, then look on google drive for the file that matches (at least partially) with that first number, then we should generate a link for that file and insert that link into another column, next to the number.
function CopiarResos() {
let ass = SpreadsheetApp.getActiveSpreadsheet();
let maxRows = ass.getSheetByName('set_de_datos_unificado').getMaxRows();
let columnaResos = ass.getSheetByName('set_de_datos_unificado').getSheetValues(2,2,maxRows,1);
let columnaLinks = ass.getSheetByName('set_de_datos_unificado').getSheetValues(2,53,maxRows,1);
let folder = DriveApp.getFolderById('1FJiet6tVgWXtM91Y7mmhgxDL2iYFm1VE'); // I change the folder ID here
let list = [];
let idList = [];
let files = folder.getFiles();
let match = '';
while (files.hasNext()){
file = files.next();
list.push(file.getName().toString());
idList.push(file.getId().toString());
}
console.log(list);
console.log(idList);
/*
let match = list.find(element => {
if (element.includes(substring)) {
return true;
}
});
*/
for (let i = 0; i in columnaResos; i++){
if(columnaLinks[i] == ""){
let substring = columnaResos[i];
match = list.find(element => {
if (element.includes(substring)) {
let idString = idList[i];
let insertStr = "https://drive.google.com/file/d/" + idString +"view";
let cellInsert = ass.getSheetByName('set_de_datos_unificado').getRange([i]+2,53);
cellInsert.setValue(insertStr);
return true;
}
});
}
};
}
getMaxRows() returns the number of rows that has the sheet (scroll down to the very bottom). You might delete the blank rows below your data or use getLastRow() instead.
References
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getmaxrows
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getlastrow
So, apart from all the thing wasnt written right turns out i was missing a bunch of lines! Mostly i was trying to get a fileid from an array even tho the indexes were completely unrelated (eg: trying to get list[300] instead of doing indexOf(match)
function copiarResos() {
let ass = SpreadsheetApp.getActiveSpreadsheet();
let maxRows = ass.getSheetByName('set_de_datos_unificado').getLastRow();
let columnaResos = ass.getSheetByName('set_de_datos_unificado').getSheetValues(2,2,maxRows,1);
let columnaLinks = ass.getSheetByName('set_de_datos_unificado').getSheetValues(2,53,maxRows,1);
let folder = DriveApp.getFolderById('1FJiet6tVgWXtM91Y7mmhgxDL2iYFm1VE'); // I change the folder ID here
let list = [];
let files = folder.getFiles();
let match = '';
while (files.hasNext()){
file = files.next();
list.push(file.getName().toString(), file.getId().toString());
}
console.log(list);
console.log(columnaLinks)
for (let i = 0; i in columnaResos; i++){
if(columnaLinks[i] == ''){
let substring = columnaResos[i];
match = list.find(element => {
if (element.includes(substring)) {
return true;
}
});
console.log(match);
matchId = list.indexOf(match);
if(matchId !== 0){
console.log(match + " id es" + matchId);
let idString = list[matchId+1];
console.log(idString)
let insertStr = "https://drive.google.com/file/d/" + idString +"/view?usp=sharing";
let cellInsert = ass.getSheetByName('set_de_datos_unificado').getRange(i+2 ,53);
cellInsert.setValue(insertStr);
}
}
match = ''
};
}

how to to convert for to foreach

jslint tell Unexpected 'for'.
so i think that i must convert for with foreach
but how?
if someone can help
thanks
// Grab the original element
var original = document.getElementsByTagName("noscript")[0];
// Create a replacement tag of the desired type
var replacement = document.createElement("span");
var i;
// Grab all of the original's attributes, and pass them to the replacement
for(i = 0, l = original.attributes.length; i < l; ++i){
var nodeName = original.attributes.item(i).nodeName;
var nodeValue = original.attributes.item(i).nodeValue;
replacement.setAttribute(nodeName, nodeValue);
}
// Persist contents
replacement.innerHTML = original.innerHTML;
// Switch!
original.parentNode.replaceChild(replacement, original);
You have a comma after i = 0, <========
it should be semicolon.
Another issue is declaring l = original.attributes.length you don't need the variable l
just use it as for(i = 0; i < original.attributes.length; ++i){
if you still wanna use a forEach you can do it as:
original.attributes.forEach(element => {
var nodeName = element.nodeName;
var nodeValue = element.nodeValue;
replacement.setAttribute(nodeName, nodeValue);
});
thanks for your answer, i got Uncaught TypeError: original.attributes.forEach is not a function
function Switch() {
var original = document.getElementsByTagName("noscript")[0];
var replacement = document.createElement("span");
original.attributes.forEach(element => {
var nodeName = element.nodeName;
var nodeValue = element.nodeValue;
replacement.setAttribute(nodeName, nodeValue);
});
// Persist contents
replacement.innerHTML = original.innerHTML;
// Switch!
original.parentNode.replaceChild(replacement, original);
}

How to begin a function only after a Parse.Object.saveAll or Parse.Promise.when

I'd like to be able to make my code a little more synchronous starting one function only after Parse has been updated. I've tried using Parse.Promise.when and Parse.Object.saveAll but it's not quite working the way I'd like as I get inconsistent behavior where the function after then begins before all of my updates have been saved.
I've reviewed the docs several times along with finding sample code on SO and other places but I'm definitely still going wrong somewhere along the way.
var goodResults = [1, 3, 5, 7];
var BlueSomething = Parse.Object.extend("something");
var query = new Parse.Query(BlueSomething);
query.find().then(function(results){
var array = [];
var array1 = [];
var array2 = [];
var array3 = [];
var resultsToEvaluate = _.map(results, function(n){
return n.get('theGoodResult')});
var newResults = _.difference(goodResults, resultsToEvaluate);
var oldResults = _.intersection(goodResults, resultsToEvaluate);
if(newResults.length > 0){
_.each(newResults, function(n){
var new = new BlueSomething();
new.set('status', n.get('someValue'));
array.push(new);})}
if(oldResults.length > 0){
_.each(oldResults, function(n){
var count = _.values(n.get('someField')).length;
var new = new BlueSomething;
if(count > 5){
n.set("full", true)
array1.push(n);
var new = new BlueSomething();
new.set("status", n.get('someValue'));
array2.push(new);
} else {
n.set("status", "updated");
array3.push(n)
}
})
}
return Parse.Object.saveAll(array, array1, array2, array3)
}).then(function(){//do other stuff only after the information
//has been saved to Parse});
It looks like you're not using the proper syntax for saveAll. The first argument should be an array of Parse objects, and the second is a callback. You could concat the arrays into one:
return Parse.Object.saveAll(array.concat(array1, array2, array3));

Dynamically choose which properties to get using Linq

I have an MVC application with a dynamic table on one of the pages, which the users defines how many columns the table has, the columns order and where to get the data from for each field.
I have written some very bad code in order to keep it dynamic and now I would like it to be more efficient.
My problem is that I don't know how to define the columns I should get back into my IEnumerable on runtime. My main issue is that I don't know how many columns I might have.
I have a reference to a class which gets the field's text. I also have a dictionary of each field's order with the exact property It should get the data from.
My code should look something like that:
var docsRes3 = from d in docs
select new[]
{
for (int i=0; i<numOfCols; i++)
{
gen.getFieldText(d, res.FieldSourceDic[i]);
}
};
where:
docs = List from which I would like to get only specific fields
res.FieldSourceDic = Dictionary in which the key is the order of the column and the value is the property
gen.getFieldText = The function which gets the entity and the property and returns the value
Obviously, it doesn't work.
I also tried
StringBuilder fieldsSB = new StringBuilder();
for (int i = 0; i < numOfCols; i++)
{
string field = "d." + res.FieldSourceDic[i] + ".ToString()";
if (!string.IsNullOrEmpty(fieldsSB.ToString()))
{
fieldsSB.Append(",");
}
fieldsSB.Append(field);
}
var docsRes2 = from d in docs
select new[] { fieldsSB.ToString() };
It also didn't work.
The only thing that worked for me so far was:
List<string[]> docsRes = new List<string[]>();
foreach (NewOriginDocumentManagment d in docs)
{
string[] row = new string[numOfCols];
for (int i = 0; i < numOfCols; i++)
{
row[i] = gen.getFieldText(d, res.FieldSourceDic[i]);
}
docsRes.Add(row);
}
Any idea how can I pass the linq the list of fields and it'll cut the needed data out of it efficiently?
Thanks, Hoe I was clear about what I need....
Try following:
var docsRes3 = from d in docs
select (
from k in res.FieldSourceDic.Keys.Take(numOfCols)
select gen.getFieldText(d, res.FieldSourceDic[k]));
I got my answer with some help from the following link:
http://www.codeproject.com/Questions/141367/Dynamic-Columns-from-List-using-LINQ
First I created a string array of all properties:
//Creats a string of all properties as defined in the XML
//Columns order must be started at 0. No skips are allowed
StringBuilder fieldsSB = new StringBuilder();
for (int i = 0; i < numOfCols; i++)
{
string field = res.FieldSourceDic[i];
if (!string.IsNullOrEmpty(fieldsSB.ToString()))
{
fieldsSB.Append(",");
}
fieldsSB.Append(field);
}
var cols = fieldsSB.ToString().Split(',');
//Gets the data for each row dynamically
var docsRes = docs.Select(d => GetProps(d, cols));
than I created the GetProps function, which is using my own function as described in the question:
private static dynamic GetProps(object d, IEnumerable<string> props)
{
if (d == null)
{
return null;
}
DynamicGridGenerator gen = new DynamicGridGenerator();
List<string> res = new List<string>();
foreach (var p in props)
{
res.Add(gen.getFieldText(d, p));
}
return res;
}

How to set i to 0 of the first item from a json criteria

I want to set i to 0 of the first item from a json criteria, eg. if the criteria is green in this case the i will start from 3... if criteria = blue it will start on 2... i need to set it to start from 0 or 1 whether it is.. also how to count total of a criteria, eg. green total is 2,, blue=1, red=2... thanks in advance!
var myBox_html ="";
var i = 0;
function createDiv(1x,2x,3x) {
A = '<something>'+1x;
B = '<something>'+2x;
C = '<something>'+3x;
myBox_html += '<something-more>'+A+B+C;
}
criteria // is a parameter from url, in this case means green
get_it = function(doc) {
var jsonData = eval('(' + doc + ')');
for (var i=0; i<jsonvar.name.length; i++) {
var 1x = jsonvar.name[i].1;
var 2x = jsonvar.name[i].2;
var 3x = jsonvar.name[i].3;
if (1x == criteria){
var Div = createDiv(1x,2x,3x);
} else {null}
}
document.getElementById("myBox").innerHTML = myBox_html;
}
get_it();
json should look like this:
var jsonvar = {"name":[{"1":"red","2":"round","3":"fruit"},{"1":"red","2":"squared","3":"box"},{"1":"blue","2":"squared","3":"box"},{"1":"green","2":"squared","3":"box"},{"1":"green","2":"pear","3":"fruit"}]};
Consider several solutions:
1: Generate criteria-grouped JSON response on the server-side. E.g.
var jsonvar = '{"name":{
"red": [{"1":"red","2":"round","3":"fruit"}, {"1":"red","2":"squared","3":"box"}],
"blue": [{"1":"blue","2":"squared","3":"box"}],
"green":[{"1":"green","2":"squared","3":"box"}, {"1":"green","2":"pear","3":"fruit"}]}}';
2: Convert you JSON array to criteria-grouped format as defined above. Here is sample routine for such a grouping:
function group_elements (arr) {
var result = {};
for (var i=0; i < arr.length; i++) {
if (!result[arr[i][1]]) {
result[arr[i][1]] = [];
}
result[arr[i][1]].push(arr[i]);
}
return result;
}
Both solutions allows you to iterate only filtered records and count length of group.

Resources