If a given array doesn't contain a given value, I wish to open a confirm dialog. The following works, however, my use of intermediate variable t seems a little excessive and I expect there is a more elegant way to do so. Could I return from the $.each loop and cause the upstream anonymous function to return false?
$(function(){
myArr=[['de'],['df','de'],['df','dz'],['de']];
if((function(){
var t=true;
$.each(myArr, function() {
console.log($.inArray('de', this)=='-1');
if($.inArray('de', this)=='-1') {t=false;return false;}; //Doesn't return true to parent
})
return t;
})() || confirm("Continue even though one of the choices doesn't contain 'de'?") ){
console.log('proceed');
}
});
You can use Array.prototype.some method, it will make code more comprehensive and simpler:
var myArr=[['de'],['df','de'],['df','dz'],['de']];
if (myArr.some(function(el) {
return el.indexOf('de') === -1;
}) && confirm("Continue even though one of the choices doesn't contain 'de'?")) {
document.write('proceed');
}
You could use grep instead, filtering out values that include 'de' and then counting the remaining:
$(function(){
var myArr=[['de'],['df','de'],['df','dz'],['de']];
var notDe = $.grep(myArr, function(item, index) {
return ($.inArray('de', this)=='-1');
});
if(notDe.length == 0 || confirm("Continue even though one of the choices doesn't contain 'de'?") ){
console.log('proceed');
}
});
Another more readable solution:
$(function () {
myArr = [
['de'],
['df', 'de'],
['df', 'dz'],
['de']
];
var t = 0;
$.each(myArr, function (k, v) {
if ($.inArray('de', v) === -1) {
t++;
}
});
if (t > 0) {
if (confirm("Continue even though " + t + " of the choices do not contain 'de'?")) {
console.log('proceed');
}
}
});
Related
My issue is a puzzling one. When I make use of ng-repeat to repeat through my data to create checkboxes it works as I expect. I will get all the things I referred to and they have duplicates. An snippet is below.
<label ng-if="project.score !='0'" data-ng-repeat="project in projects | unique:'c.projectRef' | orderBy:'c.projectRef'">
<input
id="project.c.projectRef"
type="checkbox"
ng-click="toggleSelection(project.c.projectRef)"
ng-checked="selectedRefs.indexOf(project.c.projectRef) > -1" />
<span>{{project.c.projectRef}}</span>
</label>
As you can see,'unique' is in there and I'm making use of Angular UI for that. There's numerous duplicate references and as I am using the checkboxes as a filter, I only want distinct checkboxes as opposed to as many checkboxes as the times it is referenced which is what ng-repeat alone will do.
It functions exactly like that if I remove unique but when I have unique in what happens is instead of giving me several checkboxes that are all unique, it just gives me one checkbox.
From what I've been testing, it appears this happens when you refer to (sorry I don't know the term for it) data when you have deeper references into it so project.c.projectRef as opposed to project.projectRef.
I'm stumped by this issue so any fixes as well as information on the issue would be greatly appreciated.
It was what I suspected in fact. Angular UI doesn't like nested references, you have to edit your angular-ui.js file and replace the unique function with this addition.
.filter('unique', function () {
return function (items, filterOn) {
if (filterOn === false) {
return items;
}
if ((filterOn || angular.isUndefined(filterOn)) && angular.isArray(items)) {
var hashCheck = {}, newItems = [];
var extractValueToCompare = function (item) {
if (angular.isObject(item) && angular.isString(filterOn)) {
var resolveSearch = function(object, keyString){
if(typeof object == 'undefined'){
return object;
}
var values = keyString.split(".");
var firstValue = values[0];
keyString = keyString.replace(firstValue + ".", "");
if(values.length > 1){
return resolveSearch(object[firstValue], keyString);
} else {
return object[firstValue];
}
}
return resolveSearch(item, filterOn);
} else {
return item;
}
};
angular.forEach(items, function (item) {
var valueToCheck, isDuplicate = false;
for (var i = 0; i < newItems.length; i++) {
if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
if(typeof item != 'undefined'){
newItems.push(item);
}
}
});
items = newItems;
}
return items;
Is it possible to create an array that will only allow objects of a certain to be stored in it? Is there a method that adds an element to the array I can override?
Yes you can, just override the push array of the array (let's say all you want to store are numbers than do the following:
var myArr = [];
myArr.push = function(){
for(var arg of arguments) {
if(arg.constructor == Number) Array.prototype.push.call(this, arg);
}
}
Simply change Number to whatever constructor you want to match. Also I would probably add and else statement or something, to throw an error if that's what you want.
UPDATE:
Using Object.observe (currently only available in chrome):
var myArr = [];
Array.observe(myArr, function(changes) {
for(var change of changes) {
if(change.type == "update") {
if(myArr[change.name].constructor !== Number) myArr.splice(change.name, 1);
} else if(change.type == 'splice') {
if(change.addedCount > 0) {
if(myArr[change.index].constructor !== Number) myArr.splice(change.index, 1);
}
}
}
});
Now in ES6 there are proxies which you should be able to do the following:
var myArr = new Proxy([], {
set(obj, prop, value) {
if(value.constructor !== Number) {
obj.splice(prop, 1);
}
//I belive thats it, there's probably more to it, yet because I don't use firefox or IE Technical preview I can't really tell you.
}
});
Not directly. But you can hide the array in a closure and only provide your custom API to access it:
var myArray = (function() {
var array = [];
return {
set: function(index, value) {
/* Check if value is allowed */
array[index] = value;
},
get: function(index) {
return array[index];
}
};
})();
Use it like
myArray.set(123, 'abc');
myArray.get(123); // 'abc' (assuming it was allowed)
//find value in array using function checkValue using underscoreJS _.each.
//return true, else false.
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function(arg) {
_.each(helloArr, function(helloArr, index) {
if (arg[index] === index) {
return true;
}
return false;
});
};
alert(checkValue("hola"));
The problem with your code is that, _.each will iterate through all the elements of the array and call the function you pass to it. You will not be able to come to a conclusion with that, since you are not getting any value returned from it (unless you maintain state outside _.each).
Note that the values returned from the function you pass to _.each will not be used anywhere and they will not affect the course of the program in any way.
But, instead, you can use _.some as an alternate, like this
var checkValue = function(arg) {
return _.some(helloArr, function(currentString) {
return arg === currentString;
});
};
But, a better solution would be, _.contains function for this purpose. You can use it like this
var checkValue = function(arg) {
return _.contains(helloArr, arg);
};
But, since you have only Strings in the Array, the best solution would be to use Array.prototype.indexOf, like this
var checkValue = function(arg) {
return helloArr.indexOf(arg) !== -1;
};
Try this:
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function(arr, val) {
_(arr).each(function(value) {
if (value == val)
{return console.log(true);}
else {return console.log(false);}
});
};
console.log(checkValue(helloArr,'hello'));
/* Output
false
true
false*/
I have an object of folders/files that looks like this:
{
about.html : {
path : './about.html'
},
about2.html : {
path : './about2.html'
},
about3.html : {
path : './about3.html'
},
folderName : {
path : './folderName',
children : {
sub-child.html : {
path : 'folderName/sub-child.html'
}
}
}
}
And it can go 6-7 levels deep of folders having children.
I want to find the object where path is equal to a string that I provide. Regardless of how deep it is.
I'm using underscore which only does top level:
_.findWhere(files,{path:'./about2.html'}
How can I do a deep, nested search. Does underscore have something for this or do I need to build a mixin with recursion?
This isn't the prettiest code, but I tested it out and it seems to work the way you are asking. It's setup as a lodash/underscore mixin, but can be used however. Usage would be like this:
_.findDeep(testItem, { 'path': 'folderName/sub-child.html' })
Implementation:
findDeep: function(items, attrs) {
function match(value) {
for (var key in attrs) {
if(!_.isUndefined(value)) {
if (attrs[key] !== value[key]) {
return false;
}
}
}
return true;
}
function traverse(value) {
var result;
_.forEach(value, function (val) {
if (match(val)) {
result = val;
return false;
}
if (_.isObject(val) || _.isArray(val)) {
result = traverse(val);
}
if (result) {
return false;
}
});
return result;
}
return traverse(items);
}
Instead of findWhere, use filter, which takes a function as the predicate rather than a key-value map. Use a recursive function to check the current node and possible children. Something like this:
var searchText = './about2.html';
var recursiveFilter = function(x) {
return x.path == searchText ||
( typeof x.children != 'undefined' && recursiveFilter(x.children['sub-child.html']) );
};
_.filter(files, recursiveFilter);
Edit
Assuming this works, you'll probably want to make a function getRecursiveFilter(searchText). Here's how that would look:
function getRecursiveFilter(searchText) {
var recursiveFilter = function(x) {
return x.path == searchText ||
(typeof x.children != 'undefined'
&& arguments.callee(x.children['sub-child.html']) );
};
return recursiveFilter;
}
Note that here, recursiveFilter uses arguments.callee to call itself recursively.
Here's a working demo.
This already has an accepted answer, but this other answer was very clean and perfect for my similar situation: https://stackoverflow.com/a/21600748/1913975
_.filter +_.where
Though accepted answer works, it's too generic - it searches all the properties of an object to find children. I am proposing introducing an extra parameter, called 'recursProperty' which will be considered to go deep in the object. This solution is also setup to be used as lodash/underscore mixin and extends loadash/underscore capabilities.
_.findDeep = function(collection, predicate, recursProperty){
let items = [];
_.each(collection, each => items.push(each));
return _.find(items, function(value, key, coll){
if (predicate(value, key, coll)){
return true;
} else {
_.each(value[recursProperty], each => items.push(each));
}
});
};
It can be used as any other underscore function. e.g,
_.findDeep(self.baseEntities, baseEntity => baseEntity.id === 71, 'entity');
Not providing proper value for 'recursProperty' argument or providing null/undefined will simply make the search only on first level (no going deep).
For a personal challenge, I'm implementing LINQ in JavaScript (well, a set of functions with LINQ-like functionality). However, as of right now, the functions are processing the data immediately; that's correct behavior for some functions (such as Sum or Aggregate), but incorrect for others (such as Select or While).
I'm curious if there's a construct in JavaScript that could get me the same behavior as in .Net, where no real processing happens until the collection is enumerated or a function with immediate execution is used.
Note: I believe this task (implementing LINQ in JS) has already been done. That's not the point. This is a challenge to myself from myself, which is likely to help me increase my understanding of LINQ (and, coincidentally, JS). Beyond personal edification, I'm going to be using LINQ for my job soon, may use JS for my job depending on the needs of individual projects, and I use JS for some things outside of work.
Edit: It seems I've attracted people unfamiliar with LINQ, so I suppose I should give some explanation on that front. LINQ is Language-INtegrated Query, something from .Net. LINQ allows for SQL-like queries on many data sources (including actual SQL relational databases), such as LINQ to Objects, which is what I'm trying to achieve.
One of the features of LINQ is deferred execution on many of the methods. If I have a collection customers and call var query = customers.Where(c => c.Age > 40); (or what it would end up being in JS, var query = customers.Where(function (c) { return c.Age > 40; });), the return value is an interface type, and the actual processing of the collection (returning the subset of the collection containing only customers older than 40) hasn't happened yet. When I use one of the methods without deferred execution (eg, query.First() or query.ToArray()), then all of the deferred processing happens. This could be a chain, such as customers.Where(...).Skip(5).Select(...).OrderBy(...) (each "..." being a function).
The upshot is that code like this:
var collection = [1, 2, 3, 4, 5];
var query = collection.Where(function (n) { return n % 2 == 0; });
collection.push(6);
alert(query.Max());
Would result in "6".
As an addendum, I'm currently implementing this project by prototyping my methods onto both Object and Array, iterating over the elements of this, and skipping any elements which are functions. Something like making an Enumerable class may be superior (and in fact may be required for my deferred execution plan, if something like returning a function or an anonymous object is required), but that's what I've currently got. My functions generally appear as something along these lines:
Object.prototype.Distinct = Array.prototype.Distinct = function (comparer) {
comparer = comparer || function (a, b) { return a == b; };
var result = [];
for (var idx in this) {
var item = this[idx];
if (typeof item == "function") continue;
if (!result.Contains(item, comparer)) result.push(item);
}
return result;
};
Fundamentally what you need to do is return objects from your functions rather than performing operations. The objects you return will contain the code necessary to perform the operations in the future. Consider an example use case:
var myCollection = [];
for(var i = 0; i < 100; i++) { myCollection.push(i); }
var query = Iter(myCollection).Where(function(v) { return v % 2 === 0; })
.Skip(5).Select(function(v) { return v*2; });
var v;
while(v = query.Next()) {
console.log(v);
}
We expect as output:
20
24
28
...
188
192
196
In order to do that we define the methods .Where(), .Skip(), and .Select() to return instances of classes with overridden versions of the .Next() method. Working code that supports this functionality: ( set trace to true to observe that the execution order is lazy)
var trace = false;
function extend(target, src) {
for(var k in src) {
target[k] = src[k];
}
return target;
}
function Iter(wrapThis) {
if(wrapThis.Next) {
return wrapThis;
} else {
return new ArrayIter(wrapThis);
}
}
Iter.prototype = {
constructor: Iter,
Where: function(fn) { return new WhereIter(this, fn); },
Skip: function(count) { return new SkipIter(this, count); },
Select: function(fn) { return new SelectIter(this, fn); }
};
function ArrayIter(arr) {
this.arr = arr.slice();
this.idx = 0;
}
ArrayIter.prototype = extend(Object.create(Iter.prototype),
{
constructor: ArrayIter,
Next: function() {
if(this.idx >= this.arr.length) {
return null;
} else {
return this.arr[this.idx++];
}
}
});
function WhereIter(src, filter) {
this.src = src; this.filter = filter;
}
WhereIter.prototype = extend(Object.create(Iter.prototype), {
constructor: WhereIter,
Next: function() {
var v;
while(true) {
v = this.src.Next();
trace && console.log('Where processing: ' + v);
if(v === null || this.filter.call(this, v)) { break; }
}
return v;
}
});
function SkipIter(src, count) {
this.src = src; this.count = count;
this.skipped = 0;
}
SkipIter.prototype = extend(Object.create(Iter.prototype), {
constructor: SkipIter,
Next: function() {
var v;
while(this.count > this.skipped++) {
v = this.src.Next();
trace && console.log('Skip processing: ' + v);
if(v === null) { return v; }
}
return this.src.Next();
}
});
function SelectIter(src, fn) {
this.src = src; this.fn = fn;
}
SelectIter.prototype = extend(Object.create(Iter.prototype), {
constructor: SelectIter,
Next: function() {
var v = this.src.Next();
trace && console.log('Select processing: ' + v);
if(v === null) { return null; }
return this.fn.call(this, v);
}
});
var myCollection = [];
for(var i = 0; i < 100; i++) {
myCollection.push(i);
}
var query = Iter(myCollection).Where(function(v) { return v % 2 === 0; })
.Skip(5).Select(function(v) { return v*2; });
var v;
while(v = query.Next()) {
console.log(v);
}
You also may want to look into "string lambdas" to make your queries much more readable. That would allow you to say "v*2" instead of function(v) { return v*2; }
I am not entirely clear on what exactly you wish to do, but I think what you should look into is the defineProperty method. What you would probably wish to do is then to redefine the .length property and execute the code only once it's read. Or if you want to do it only once the property itself is read do it at that point. Not sure how LINQ works or even what it is, so that's why I am a bit vague. Either way, with defineProperty you can do something like
Object.defineProperty(o, "a", { get : function(){return 1;});
Allowing you to do actions only once the property is accessed (and you can do a lot more than that as well).