I wrote my code to search string for keywords and extracting needed data, but I have problems when I'm trying to search with keywords in arrays named: sort, title and artist. When I'm doing it I get an error about potential infinite loop.
var type = ['track','tracks','song','songs','album','albums'];
var artist = ['created by', 'made by'];
var genre = ['genre'];
var limit = ['set limit'];
var title = ['title','name'];
var sort = ['sort by', 'classificate by', 'separate by'];
var sortBy = ['popularity'];
// function changeWordsToNumbers(words) {
// if (msg.numbers[words])
// return msg.numbers[words];
// }
function searchForIndex(instruction, keywords) {
for (i = 0; i < keywords.length; i++) {
let search = instruction.search(keywords[i]);
if (search)
return search;
}
return false;
}
function getSearchResult(wanted) {
var searchResult = {
artist : searchForIndex(wanted, artist),
genre : searchForIndex(wanted, genre),
limit : searchForIndex(wanted, limit),
type : searchForIndex(wanted, type),
title : searchForIndex(wanted, title),
sort : searchForIndex(wanted, sort),
sortBy : searchForIndex(wanted, sortBy)
};
return searchResult;
}
function removeJunkKeyword(searchResult,instruction) {
for(var property in searchResult) {
if(searchResult.hasOwnProperty(property)) {
if(searchResult[property] != - 1) {
instruction = instruction.slice(0, searchResult[property] - 1);
}
}
}
return instruction;
}
function checkExist(searchResult) {
for(var property in searchResult) {
if(searchResult.hasOwnProperty(property)) {
if(searchResult[property] != -1)
return false;
}
}
return true;
}
function findAndCleanQuery(instruction, keywords) {
var exist = instruction.search(keywords);
var result = instruction.slice(exist + keywords.length + 1, instruction.length);
var searchResult = getSearchResult(result);
if (exist != -1) {
if (checkExist(searchResult)) {
return result;
} else {
result = removeJunkKeyword(searchResult,result);
return result;
}
}
return false;
}
function searchFor(instruction, keywords) {
for (i = 0; i < keywords.length; i++) {
let result = findAndCleanQuery(instruction,keywords[i]);
if (result)
return result;
}
return false;
}
function searchForType(instruction) {
for (i = 0; i < type.length; i++) {
let search = instruction.search(type[i])
if(search)
return type[i];
}
return false;
}
function searchForKeywords(instruction) {
msg.artist = searchFor(instruction, artist);
msg.type = searchForType(instruction, type);
msg.genre = searchFor(instruction, genre);
msg.limit = searchFor(instruction, limit);
msg.title = searchFor(instruction, title);
msg.sort = searchFor(instruction, sortreg);
}
var msg = {}
msg.instruction = 'Search for me';
searchForKeywords(msg.instruction);
console.log(msg.artist);
console.log(msg.type);
console.log(msg.genre);
console.log(msg.limit);
console.log(msg.title);
console.log(msg.sort);
Link for code: https://repl.it/J4Mc/9
PS. The object msg is used by node-red to communicate between nodes.
The issue is that you're doing this on several of your loops:
for (i = 0; i < keywords.length; i++) {
...where you should be doing this instead:
for (let i = 0; i < keywords.length; i++) {
Without using let, the variable i is effectively global. Each time you went into a new loop it got reset to 0, so it was never able to increase, which created the infinite loop.
As a side note, you'll also notice sortreg is undefined when it's used on line 98.
Related
I created a basket in JavaScript, it works perfectly except for one thing:
When we add products to the basket, we select a quantity and everything goes well, but if we click again to add the same product, instead of modifying the quantity of the product, the same product is added to the localStorage.
To avoid this problem, I have to search for the product in the localStorage with the Array.find() method, if the product exists, I modify the quantity, otherwise I add it. But I don't know how to do it the right way yet
let basket = JSON.parse(localStorage.getItem('monPanier')) || [];
let wishlist = JSON.parse(localStorage.getItem('myWishList')) || [];
let totalBasket = 0;
let nbrArticles;
let nbrWishlist;
function addBasket()
{
let quantity_cart = $(".input_quantity"+$(this).data("id")).val();
let name_article_cart = $(this).data("name");
let idArticle = parseInt(this.id.replace("add_basket_btn_",""));
let price_cart = $(this).data("price");
let total_price_cart = (quantity_cart * price_cart);
let newBasket = [idArticle,quantity_cart,name_article_cart,price_cart,total_price_cart];
basket.push(newBasket);
saveStorageBasket();
loadStorageBasket();
showBasket();
calculeNbrArticle();
console.log(newBasket);
}
function addWishlist()
{
let name_article_wishlist = $(this).data("name");
let idArticle = parseInt(this.id.replace("add_wishlist_btn_",""));
let price_article_wishlist = $(this).data("price");
let newWishlist = [idArticle,name_article_wishlist,price_article_wishlist];
wishlist.push(newWishlist);
saveStorageWishlist();
loadStorageWishlist();
showWishList();
calculeNbrWishlist();
}
function showBasket() {
//vider le panier
$("#basket").empty();
totalBasket = 0;
for(let i=0; i < basket.length; i++)
{
totalBasket = totalBasket + basket[i][4];
$("#basket").append("<tr><td>"+basket[i][1]+"</td>"
+"<td>"+basket[i][2]+"</td>"
+"<td>"+basket[i][3]+"€</td>"
+"<td>"+basket[i][4]+"€</td>"
+"<td><button data-id='"+i+"' class='btn_cancel_basket'>supprimer</button></td>"
+"</tr>");
}
// installer un event
$("#basket button.btn_cancel_basket").on("click",suppArticleBasket);
$("#totalBasket").text(totalBasket+" €");
}
function suppArticleBasket()
{
let index = $(this).data('id');
console.log(index);
basket.splice(index,1);
saveStorageBasket();
loadStorageBasket();
showBasket();
}
function showWishList()
{
for(let i=0; i < wishlist.length; i++)
{
$("#Wishlist").append("<tr><td>"+wishlist[i][1]+"</td>"
+"<td>"+wishlist[i][2]+"</td>"
+"</tr>");
}
}
function emptyBasket()
{
if(basket == null || basket == 0)
{
console.log("panier = true");
return true;
}else
{
console.log("panier = false");
return false;
}
}
function validateBasket()
{
if(emptyBasket() == false)
{
loadStorageBasket();
basket = JSON.stringify(basket); //js --> json
console.log($.get("index.php","action=appelAjax2&Basket="+basket+"&total="+totalBasket,resetBasket));
}
}
function resetBasket(reponse){
$("#confirmationcmd").html("<p>Commande validée.</p>");
$("#basket").empty();
basket.length = 0;
localStorage.clear();
loadStorageBasket();
showBasket();
totalBasket = 0;
calculeNbrArticle();
}
function resetBasket2(reponse){
$("#confirmationcmd").html("<p>Panier annulé.</p>");
$("#basket").empty();
basket.length = 0;
localStorage.clear();
loadStorageBasket();
showBasket();
totalBasket = 0;
calculeNbrArticle();
}
function resetWishlist(reponse){
$("#confirmationWishlist").html("<p>Wishlist remise à zéro.</p>");
$("#wishlist").empty();
wishlist.length = 0;
localStorage.clear();
loadStorageWishlist();
showWishList();
totalWishlist = 0;
calculeNbrWishlist();
}
function saveStorageWishlist(){
localStorage.setItem("myWishList", JSON.stringify(wishlist));
}
function loadStorageWishlist(){
wishlist = localStorage.getItem("myWishList");
if(wishlist == null)
{
wishlist =[];
}
else
{
wishlist = JSON.parse(wishlist);
}
}
function calculeNbrWishlist()
{
loadStorageWishlist();
nbrWishlist = wishlist.length;
$("#nbrWishlist").text("("+nbrWishlist+")");
}
function saveStorageBasket(){
localStorage.setItem("monPanier", JSON.stringify(basket));
}
function loadStorageBasket(){
basket = localStorage.getItem("monPanier");
if(basket == null)
{
basket =[];
}
else
{
basket = JSON.parse(basket);
}
}
function calculeNbrArticle()
{
loadStorageBasket();
nbrArticles = basket.length;
$("#nbrArticles").text("("+nbrArticles+")");
}
$(function()
{
calculeNbrArticle();
calculeNbrWishlist();
loadStorageBasket();
loadStorageWishlist();
showBasket();
showWishList();
$(".add_basket_btn").on('click',addBasket);
$(".btn_wishlist").on('click',addWishlist);
$("#validateButton").on('click',validateBasket);
$("#resetButton").on('click',resetBasket2);
$("#resetButtonWishlist").on('click',resetWishlist);
});
The basic idea:
push the new basket array if empty
check if the ID is already in the basket array
if so, update the quantity
if not, add the new basket
I have read the following learning resources about:
for loops (w3schools)
find (MDN)
filter (MDN)
According to what I have read in this post Array.find does not work because it finds only the first element. I used the suggested filter option.
The essential part with changes:
function updateQuantity(basket, newBasket, idArticle)
{
// filter: search for articleId in array of arrays
if (basket.filter(({ 1: n }) => n === idArticle)) {
for(i = 0; i < basket.length; i++) {
basket[i][1] += 1;
}
} else {
basket.push(newBasket)
}
}
function addBasket()
{
let quantity_cart = 1
let name_article_cart = "productName"
let idArticle = 1
let price_cart = 10
let total_price_cart = (quantity_cart * price_cart);
let newBasket = [idArticle,quantity_cart,name_article_cart,price_cart,total_price_cart];
if (basket.length === 0) {
basket.push(newBasket)
}
else
{
updateQuantity(basket, newBasket, idArticle)
}
console.log(basket)
}
My complete test code:
let basket = JSON.parse(localStorage.getItem('monPanier')) || [];
let totalBasket = 0;
let nbrArticles;
function updateQuantity(basket, newBasket, idArticle)
{
// filter: search for articleId in array of arrays
if (basket.filter(({ 1: n }) => n === idArticle)) {
for(i = 0; i < basket.length; i++) {
basket[i][1] += 1;
}
} else {
basket.push(newBasket)
}
}
function addBasket()
{
let quantity_cart = 1
let name_article_cart = "productName"
let idArticle = 1
let price_cart = 10
let total_price_cart = (quantity_cart * price_cart);
let newBasket = [idArticle,quantity_cart,name_article_cart,price_cart,total_price_cart];
if (basket.length === 0) {
basket.push(newBasket)
}
else
{
updateQuantity(basket, newBasket, idArticle)
}
console.log(basket)
}
function suppArticleBasket()
{
let index = 1
console.log(index);
basket.splice(index,1);
saveStorageBasket();
loadStorageBasket();
}
function emptyBasket()
{
if(basket == null || basket == 0)
{
return true;
} else
{
return false;
}
}
function saveStorageBasket(){
localStorage.setItem("monPanier", JSON.stringify(basket));
}
function loadStorageBasket(){
basket = localStorage.getItem("monPanier");
if(basket == null)
{
basket =[];
}
else
{
basket = JSON.parse(basket);
}
}
$(function()
{
loadStorageBasket();
$("#add_basket_btn").on('click',addBasket);
});
<button id="add_basket_btn">Add</button>
I've been writing a neural network from scratch to learn from.
but since i'm still learning - I want to make sure what I'm writing is actually correct.
I have an array of arrays (a matrix), of cell objects. attached to a 'brain' object which has the following method two methods:
train: function(data)
{
for (let b=0; b< data.length; b++)// for the length of the training data - I.E. we are going assume we are getting many relatively shortly indexed arrays
{
if(data[b].answers.length != data[b].questions.length)
{
console.log("bad data");
return false;
}
for(let c=0;c<data[b].questions.length;c++)
{
brain.attachInputLayer(data[b].questions[c]);
brain.updateForward();
let direction = brain.determinDirection(data[b].answers[c]); //return an array of updateObject with determined weights bias value adjustments, which each cell gets updated order should be from generation by column;
brain.cellMatrix.forEach(cellArray=> cellArray.forEach(cell=> cell.adjust(direction.find(x=> x.ID ===cell.ID))));
brain.updateForward();
brain.displayBrain();
}
}
console.log("all training data done");
alert("win?");
console.log(brain.cellMatrix);
console.log("brain");
}
and
determinDirection:function(answer)
{
// answer is the array of values of each answer cell we want as a result
let arrayOfUpDateObjectsForCell = [];
for(let e=0; e<answer.length; e++)
{
let answerCell = brain.cellMatrix[cellMatrix.length-1][e];
let returnBucket = [];
arrayOfUpDateObjectsForCell.push(answerCell.whatIwant(answer[e], returnBucket));
}
let list = Flat(arrayOfUpDateObjectsForCell);
let realList = Clean(list);
return realList;
}
so each cell of the last generation (the answer output) calls the whatIwant method at brain.train(), this function propagates backwards through the network... but my question really is this:::
:::
does it look like I am calculating the error / direction to move each value correctly?
is averaging the changes between the duplicated updateObjects correct?
(the desiredObjectchange for cell.gen=3,order=0 gets created from each of the next layer cells calling whatIwant. the changes cell.gen=4,order=0 wants cell.gen=3,order=0 to have is averaged with the changes cell.gen=4,order=1 wants for cell.gen=3,order=0).
is averaging the correct operation here?
:::
whatIwant:function(answerValue, returnArray)
{
let desiredWeights = this.weights;
let desiredBias = this.bias;
let desiredActivations = this.activations;
let error = (1/2)*Math.pow(cell.value-answerValue,2);
let desiredObjectChange =
{
ID:this.ID,
weights:this.weights,
bias:this.bias,
activations:this.activations,
value:answerValue,
combine:function(yourCloneFriend)
{
if(yourCloneFriend == false)
{
return;
}
this.bias = (1/2)*(this.bias+yourCloneFriend.bias);
let cWeight = yourCloneFriend.weights[Symbol.iterator]();
let cActivations = yourCloneFriend.activations[Symbol.iterator]();
this.weights.forEach(x=> (1/2)*(x+cWeight.next().value));
this.activations.forEach(y=> (1/2)*(y+cActivations.next().value));
this.recalculateValue();
return this;
},
recalculateValue:function()
{
this.value = Sigmoid(arrayMultiply(this.weights, this.activations)+this.bias);
}
}
for(let k = 0; k< this.weights.length; k++)
{
let lastValue = Sigmoid(arrayMultiply(desiredWeights, desiredActivations)+desiredBias);
let lastError = (1/2)*Math.pow(lastValue-answerValue,2);
for(let l=0;l<3;l++)
{
let currentValue = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
let currentError = (1/2)*Math.pow(currentValue-answerValue,2);
let positiveRange = false;
if(desiredWeights[k] < 0){ positiveRange = true;}
let nudgedWeightArray = NudgeArray(desiredWeights, k, l, positiveRange); //returns a copy array to test, with weight adjusted.
let testWeightChange = Sigmoid(arrayMultiply(nudgedWeightArray,desiredActivations)+desiredBias);
let testWeightError = (1/nudgedWeightArray.length)*Math.pow(testWeightChange - answerValue, 2);
let testWeightResult = compareSmallnumbers('isSmaller', currentError, testWeightError);
if(testWeightResult);
{
desiredWeights = nudgedWeightArray;
currentError = testWeightError;
}
positiveRange=false;
if(desiredBias < 0){positiveRange = true;}
let nudgedBiasVal = this.nudge(desiredBias,l,positiveRange);
let testBiasChange = Sigmoid(nudgedBiasVal+desiredWeights[k]*desiredActivations[k]);
let testBiasError = (1/1)*Math.pow(testBiasChange - answerValue, 2);
let testBiastResult = ('isSmaller', currentError, testBiasError);
if(testBiastResult);
{
desiredBias = nudgedBiasVal;
currentError = testBiasError;
}
positiveRange=!!Math.random(0,1)>5;
let nudgedAcitivationArray = NudgeArray(desiredActivations,k,l,positiveRange);
let testActivationChange = Sigmoid(arrayMultiply(nudgedAcitivationArray,desiredWeights)+desiredBias);
let testActivationError = (1/nudgedAcitivationArray.length)*Math.pow(testActivationChange - answerValue, 2);
let testActivationResult = compareSmallnumbers('isSmaller', currentError, testActivationError);
if(testActivationResult);
{
desiredActivations[k] = nudgedAcitivationArray[k];
currentError = testActivationError;
}
//and the end of the loop, update the error to the new value
let errorResult = compareSmallnumbers('isSmaller',lastError, currentError);
if(errorResult)
{
lastError = currentError;
}
}
desiredObjectChange.weights[k] = desiredWeights[k];
desiredObjectChange.bias = desiredBias;
desiredObjectChange.activations[k] = desiredActivations[k];
desiredObjectChange.value = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
}
let combineObject = returnArray.find(x=>x.ID === desiredObjectChange.ID);
if(!combineObject)
{
returnArray.push(desiredObjectChange);
}
//that was this object - simple stuff, now we need to call this function
if(Array.isArray(cell.lastGenerationTargetKeys) && cell.lastGenerationTargetKeys.length)
{
let nextActivation = desiredObjectChange.activations[Symbol.iterator]();
brain.cellMatrix[cell.generation-1].forEach(x=> x.whatIwant(nextActivation.next().value, returnArray));
return returnArray;
}
else
{
return;
}
},
clean,flat and NudgeArray are these::
function Clean(array)
{
let rArray = [];
array.forEach((x)=>
{
let search = rArray.find(y=>y.ID ===x.ID);
if(search === undefined)
{
rArray.push(x);
}
else
{
rArray[rArray.indexOf(search)].combine(x);
}
});
return rArray;
}
function Flat(array)
{
let holdBucket = [];
let flatten = function(array)
{
for(let i = 0; i<array.length;i++)
{
if(Array.isArray(array[i]))
{
flatten(array[i]);
}
else
{
holdBucket.push(array[i]);
}
}
}
flatten(array);
return holdBucket;
}
function NudgeArray(array ,arrayIndex, Nudgeindex, isPositive)
{//nudge index is designed to act like a variable learning rate modifier, as it tests, jumps decrease in size
let returnArray = [];
array.forEach(x=>returnArray.push(x));
let value = returnArray[arrayIndex];
if(isPositive)
{
value+=(Math.random(0,1)/(Nudgeindex+3));
value = Sigmoid(value);
}
else
{
value+=(Math.random(-1,1)/(Nudgeindex+3));
value = Sigmoid(value);
}
returnArray.splice(arrayIndex,1,value);
return returnArray;
}
I'd like to extend javascript to add custom type checking.
e.g.
function test(welcome:string, num:integer:non-zero) {
console.log(welcome + num)
}
which would compile into:
function test(welcome, num) {
if(Object.prototype.toString.call(welcome) !== "[object String]") {
throw new Error('welcome must be a string')
}
if (!Number.isInteger(num)) {
throw new Error('num must be an integer')
}
console.log(welcome + num)
}
What's the most straightforward way of doing this?
So far i've looked at:
sweet.js (online documentation looks out of date as I think it's going through some sort of internal rewrite)
esprima and escodegen (not sure where to start)
manually parsing using regular expressons
After evaluating all the various options, using sweet.js appears to be the best solution. It's still fairly difficult to get working (and I am probably doing stuff the wrong way) but just in case someone want's to do something similar this here was my solution.
'use strict'
syntax function = function(ctx) {
let funcName = ctx.next().value;
let funcParams = ctx.next().value;
let funcBody = ctx.next().value;
//produce the normal params array
var normalParams = produceNormalParams(funcParams)
//produce the checks
var paramChecks = produceParamChecks(funcParams)
//produce the original funcBody code
//put them together as the final result
var params = ctx.contextify(funcParams)
var paramsArray = []
for (let stx of params) {
paramsArray.push(stx)
}
var inner = #``
var innerStuff = ctx.contextify(funcBody)
for (let item of innerStuff) {
inner = inner.concat(#`${item}`)
}
var result = #`function ${funcName} ${normalParams} {
${paramChecks}
${inner}
}`
return result
function extractParamsAndParamChecks(paramsToken) {
var paramsContext = ctx.contextify(paramsToken)
//extracts the actual parameters
var paramsArray = []
var i = 0;
var firstItembyComma = true
for (let paramItem of paramsContext) {
if (firstItembyComma) {
paramsArray.push({
param: paramItem,
checks: []
})
firstItembyComma = false
}
if (paramItem.value.token.value === ',') {
firstItembyComma = true
i++
} else {
paramsArray[i].checks.push(paramItem.value.token.value)
}
}
for (var i = 0; i < paramsArray.length; i++) {
var checks = paramsArray[i].checks.join('').split(':')
checks.splice(0, 1)
paramsArray[i].checks = checks
}
return paramsArray
}
function produceNormalParams(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
//Produces the final params #string
var inner = #``
var first = true
for (let item of paramsArray) {
if (first === true) {
inner = inner.concat(#`${item.param}`)
} else {
inner = inner.concat(#`,${item.param}`)
}
}
return #`(${inner})`
}
function produceParamChecks(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
var result = #``
for (let paramObject of paramsArray) {
var tests = produceChecks(paramObject)
result = result.concat(#`${tests}`)
}
return result
}
function produceChecks(paramObject) {
var paramToken = paramObject.param
var itemType = paramObject.checks[0]
var checks = paramObject.checks
if (itemType === undefined) return #``
if (itemType === 'array') {
return #`if (Object.prototype.toString.call(${paramToken}) !== "[object Array]") throw new Error('Must be array:' + ${paramToken})`
else {
throw new Error('item type not recognised: ' + itemType)
}
}
}
module.filter('myCustomFilter', function ($filter) {
return function(items, searchedTxt, headers) {
if (headers.choice === "option1") {
return resultByDates(items, searchedTxt);
} else if (headers.choice === "option2") {
return resultByName(items, searchedTxt, headers);
}
else if (headers.choice === "option3") {
return resultSimple(items, searchedTxt);
}
return items;
};
function resultByDates(items, search) {
if (search === undefined || search === null)
return items;
var k = Object.keys(search)[0];
var i;
for (i = 0; i < items.length; i++) {
items[i][k] = $filter('date')(items[i][k], "MM/dd/yyyy");
}
var filteredData = $filter('filter')(items, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(items.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
function resultByName(items, search, headers) {
if (search === undefined || search === null)
return items;
if (headers !== undefined) {
var k = Object.keys(search)[0];
var i;
var componentVals = headers.componentVals;
var itemsCopy = angular.copy(items);
for (i = 0; i < items.length; i++) {
for (var obj in componentVals) {
if (componentVals[obj].ID === itemsCopy[i][k]) {
itemsCopy[i][k] = componentVals[obj].Name;
break;
}
}
}
var filteredData = $filter('filter')(itemsCopy, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(itemsCopy.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
return items;
}
function resultSimple(items, search) {
if (search === undefined || search === null)
return items;
return $filter('filter')(items, search);
}
});
Guys, I have above filter which works - partially, Option1 and Option3 returns correct filtered data, but there is some problem with Option2.
When I filter data with Option1 it returns correctly filtered data, then I can additionally filter with Option3 and it filters incorrectly returned previously data. When I use Option2 it seems like the data is being returned is not binded with the previous return, it's returning separate data.It seems like it is a separate collection... Is there something wrong with the way I return data in Option2?
Hope I have explained problem sufficiently. Thanks.
Below version with some fixes suggested by Himmel.
module.filter('myCustomFilter', function ($filter) {
return function (items, searchedTxt, headers) {
var key;
var i;
var indexes;
var filteredData;
var output;
if (headers.choice === "option1") {
key = Object.keys(searchedTxt)[0];
for (i = 0; i < items.length; i++) {
items[i][key] = $filter('date')(items[i][key], "MM/dd/yyyy");
}
filteredData = $filter('filter')(items, searchedTxt);
indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(items.indexOf(filteredData[i]));
}
output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
} else if (headers.choice === "option2") {
key = Object.keys(searchedTxt)[0];
var componentVals = headers.componentVals;
var itemsCopy = angular.copy(items);
for (i = 0; i < items.length; i++) {
for (var obj in componentVals) {
if (componentVals[obj].ID === itemsCopy[i][key]) {
itemsCopy[i][key] = componentVals[obj].Name;
break;
}
}
}
filteredData = $filter('filter')(itemsCopy, searchedTxt);
indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(itemsCopy.indexOf(filteredData[i]));
}
output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
} else if (headers.choice === "option3") {
return $filter('filter')(items, searchedTxt);
}
return items;
};
});
I have finally figured out the problem… So when I filtered with Option3 there was no issue because I didn’t modify collection before filtering, after when I filtered with Option1 I did mods to the collection because I wanted to filter based on displayed formatted date. Finally when I used Option3 I was only modifying collection for needs of current Option3 filter – without considering that before filtering I should consider mods that I have done during Option2 filtering. To confirm/test I have created a global array which holds my modified collection, so every time I’m filtering with any of the options I’m using that global array. It’s very dirty temporary solution but it works. Hope I clarified this problem enough. Is there a better solution?
It seems like you're trying to combine learning JavaScript and Angular at the same time, tough times!
module.filter('myCustomFilter', function ($filter) {
// You probably aren't trying to return "function", here
return function(items, searchedTxt, headers) {
if (headers.choice === "option1") {
return resultByDates(items, searchedTxt);
} else if (headers.choice === "option2") {
return resultByName(items, searchedTxt, headers);
}
else if (headers.choice === "option3") {
return resultSimple(items, searchedTxt);
}
return items;
};
// are you declaring another function after you've returned?
function resultByDates(items, search) {
if (search === undefined || search === null)
return items;
var k = Object.keys(search)[0];
var i;
for (i = 0; i < items.length; i++) {
items[i][k] = $filter('date')(items[i][k], "MM/dd/yyyy");
}
var filteredData = $filter('filter')(items, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(items.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
// another??
function resultByName(items, search, headers) {
if (search === undefined || search === null)
return items;
if (headers !== undefined) {
var k = Object.keys(search)[0];
var i;
var componentVals = headers.componentVals;
var itemsCopy = angular.copy(items);
for (i = 0; i < items.length; i++) {
for (var obj in componentVals) {
if (componentVals[obj].ID === itemsCopy[i][k]) {
itemsCopy[i][k] = componentVals[obj].Name;
break;
}
}
}
var filteredData = $filter('filter')(itemsCopy, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(itemsCopy.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
return items;
}
// christ!!
function resultSimple(items, search) {
if (search === undefined || search === null)
return items;
return $filter('filter')(items, search);
}
});
So, the first line, function($filter) {... is the function that is "invoked" when the list filter is triggered. The value that it returns will be the values that constitute the new list.
So if you "return" [1, 2, 3, 4] you will see those in the list in the browser. But if your function immediately returns a "function" instead of a "list" ([]), well then the browser will probably do something weird with it...
Oh wait, you're calling a bunch of functions that are outside of the current function you're in. Probably remove resultByDates from the current function call, as well as resultByName..., put them as siblings to module.filter.
This question is very hard to fix with so many issues, can you isolate a simpler problem? Maybe a small piece of unexpected behavior?
Need help with the chaining. The functions work. But async calls make it hard for me to get everything. Help me think right!
My thought:
Get All Webs recursively (function works)
Get all lists from webs and iff announcementlist add to array and pass along
Get two items from all announcmentlists and sort by created.
Add ALL announcement items into one large array (to be able to sort array later.
Heres the code,
function getAllWebs(success, error) {
var ctx = SP.ClientContext.get_current();
var web = ctx.get_site().get_rootWeb();
var result = [];
var level = 0;
result.push(web);
var getAllWebsInner = function (web, result, success, error) {
level++;
var ctx = web.get_context();
var webs = web.get_webs();
ctx.load(webs, 'Include(Title,Webs,ServerRelativeUrl)');
ctx.executeQueryAsync(
function () {
for (var i = 0; i < webs.get_count() ; i++) {
var web = webs.getItemAtIndex(i);
result.push(web);
if (web.get_webs().get_count() > 0) {
getAllWebsInner(web, result, success, error);
}
}
level--;
if (level == 0 && success)
success(result);
},
error);
};
getAllWebsInner(web, result, success, error);
}
function error(sender, args) {
console.log(args.get_message());
};
function getAnnouncementLists(web, success, error) {
var dfd = $.Deferred();
var ctx = web.get_context();
var collList = web.get_lists();
var result = []
ctx.load(collList, 'Include(Title, Id, BaseTemplate)');
ctx.executeQueryAsync(function () {
for (var i = 0; i < collList.get_count() ; i++) {
var list = collList.getItemAtIndex(i);
var bTemp = list.get_baseTemplate();
if (bTemp == 104) {
result.push(list);
}
}
//success(result);
dfd.resolve(result);
}, error);
return dfd.promise();
}
function getListItems(list, success, error) {
var dfd = $.Deferred();
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><OrderBy><FieldRef Name="Created" Ascending="False"></FieldRef>'
+ '</OrderBy></Query><ViewFields><FieldRef Name="Title"/><FieldRef Name="Body"/>' +
'<FieldRef Name="Created"/></ViewFields><RowLimit>2</RowLimit></View>');
var listItems = list.getItems(camlQuery);
var result = []
var ctx = list.get_parentWeb().get_context();
ctx.load(listItems);
ctx.executeQueryAsync(function () {
for (var i = 0; i < listItems.get_count() ; i++) {
var item = listItems.getItemAtIndex(i);
result.push(item);
}
dfd.resolve(result);
//success(result);
}, error);
return dfd.promise();
}
function printResults(items) {
var sortedItems = items.sort(dynamicSort("get_created()"));
alert(sortedItems);
}
function dynamicSort(property) {
var sortOrder = 1;
if (property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a, b) {
var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result * sortOrder;
}
}
$(document).ready(function () {
var items = getAllWebs(
function (allwebs) {
var array = [];
for (var i = 0; i < allwebs.length; i++) {
getAnnouncementLists(allwebs[i]).then(function (announceLists) {
for (var i = 0; i < announceLists.length; i++) {
getListItems(announceLists[i]).then(function (items) {
array.push(items);
});
}
});
}
return array;
}
);
//getAllWebs(
// function (allwebs) {
// for (var i = 0; i < allwebs.length; i++) {
// getAnnouncementLists(allwebs[i],
// function (announceLists) {
// for (var i = 0; i < announceLists.length; i++) {
// getListItems(announceLists[i],
// function (items) {
// printResults(items);
// }, error);
// }
// }, error);
// }
// }, error);
});
Given the requirements to retrieve list items from Announcements lists located across site collection, below is demonstrated the modified example that contains some improvements such as:
the number of requests to the server is reduced
fixed the issue in getAllWebs function that prevents to return any results if site contains only a root web
Example
function getAllWebs(propertiesToRetrieve,success, error) {
var ctx = SP.ClientContext.get_current();
var web = ctx.get_site().get_rootWeb();
var result = [];
var level = 0;
ctx.load(web, propertiesToRetrieve);
result.push(web);
var getAllWebsInner = function (web, result, success, error) {
level++;
var ctx = web.get_context();
var webs = web.get_webs();
var includeExpr = 'Include(Webs,' + propertiesToRetrieve.join(',') + ')';
ctx.load(webs, includeExpr);
ctx.executeQueryAsync(
function () {
for (var i = 0; i < webs.get_count() ; i++) {
var web = webs.getItemAtIndex(i);
result.push(web);
if (web.get_webs().get_count() > 0) {
getAllWebsInner(web, result, success, error);
}
}
level--;
if (level == 0 && success)
success(result);
},
error);
};
getAllWebsInner(web, result, success, error);
}
function loadListItems(lists,query,success,error,results){
var results = results || [];
var curList = lists[0];
var ctx = curList.get_context();
var listItems = curList.getItems(query);
ctx.load(listItems);
ctx.executeQueryAsync(function () {
results.push.apply(results, listItems.get_data());
lists.shift();
if(lists.length > 0) {
loadListItems(lists,query,success,error,results);
}
if(lists.length == 0)
success(results);
}, error);
}
function dynamicSort(property) {
var sortOrder = 1;
if (property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a, b) {
var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result * sortOrder;
}
}
var propertiesToRetrieve = ['Lists.Include(BaseTemplate)','ServerRelativeUrl'];
getAllWebs(propertiesToRetrieve,
function(allwebs){
//1. get filtered lists
var allAnnouncementLists = [];
allwebs.forEach(function(w){
var announcementLists = w.get_lists().get_data().filter(function(l){
if(l.get_baseTemplate() == SP.ListTemplateType.announcements)
return l;
});
allAnnouncementLists.push.apply(allAnnouncementLists, announcementLists);
});
//2.Load list items from lists
var query = new SP.CamlQuery(); //<-set your custom query here
loadListItems(allAnnouncementLists,query,
function(allListItems){
//3.Sort and print results
var sortedItems = allListItems.sort(dynamicSort("get_created()"));
sortedItems.forEach(function(item){
console.log(item.get_item('Title'));
});
},logError);
},
logError);
function logError(sender,args){
console.log(args.get_message());
}