How to filter an array? - javascript

Guys, please don't answer me to use a JavaScript library to solve this problem, I'm using VanillaJS.
Suppose I have an array with 10,000 string records, same as following:
var arr = [
'John',
'Foo',
'Boo',
...
'Some',
'Beer'
];
Please note that the array doesn't follow any sort.
Now, I want to find items with oo in the text, what is the best way to do? Should I populate a new array or just pop items that don't match with the criteria?

You can make use of the filter method, which will create a new array with all the elements that passes the condition.
arr.filter(function(x){ return x.indexOf ('oo') > -1});
If you want to use filter method in every browser you could add the polyfill method (see link) in your code.
Another option (slightly faster) with basic javascript would be:
Looping trough the array with a simple for loop and test on your condition.
var filtered = [];
for(var i=0, length=arr.length; i<length; i++){
var current = arr[i];
if(current.indexOf('oo') > -1){
filtered.push(current);
}
}

my approch
forEach function
function forEach(array, action) {
for(var i=0; i<array.length; i++)
action(array[i]);
}
partial function
function asArray(quasiArray, start) {
var result = [];
for(var i = (start || 0); i < quasiArray.length; i++)
result.push(quasiArray[i]);
return result;
}
function partial(func) {
var fixedArgs = asArray(arguments, 1);
return function() {
return func.apply(null, fixedArgs.concat(asArray(arguments)));
};
}
contains method for String obj
if (!String.prototype.contains) {
String.prototype.contains = function (arg) {
return !!~this.indexOf(arg);
};
}
filter function:
function filter(test, array) {
var result = [];
forEach(array, function(element) {
if (test(element))
result.push(element);
});
return result;
}
test function for test array items
function test(key, el) {
return el.contains(key);
}
finally
filter(partial(test, 'oo'), arr);

NO shortcuts ( p.s. you said I want to find items , not filter - hence my answer)
simple loop :
var g=arr.length; //important since you have big array
for( var i=0;i<g;i++)
{
if ( g[i].indexOf('oo')>-1)
{
console.log(g[i]);
}
}
If you want to filter (without polyfill)
var g=arr.length; //important since you have big array
var h=[];
for( var i=0;i<g;i++)
{
if ( g[i].indexOf('oo')>-1)
{
h.push(g[i]);
}
}
//do something with h

A very simple way, use forEach:
var result = [];
arr.forEach(function (value) {
if (value.indexOf('oo') !== -1) {
result.push(value);
}
});
or map:
var result = [];
arr.map(function (value) {
if (value.indexOf('oo') !== -1) {
result.push(value);
}
});

Related

Why would my find method return undefined?

I'm recreating a number of Underscore.js methods to study JavaScript and programming in general.
Below is my attempts to recreate Underscore's _.find() method.
var find = function(list, predicate) { // Functional style
_.each(list, function(elem){
if (predicate(elem)) {
return elem;
}
});
};
var find = function(list, predicate) { // Explicit style
if (Array.isArray(list)) {
for (var i = 0; i < list.length; i++) {
if (predicate(list[i])) {
return list[i];
}
}
} else {
for (var key in list) {
if (predicate(list[key])) {
return list[key];
}
}
}
};
My second find method, which is using for loop and for in loop works. Whereas, my first find method would return undefined. I believe both should do the same work. However, they don't. Would someone please point what is going on?
Your return is only returning from the inner (nested) function and your find function is indeed not returning anything, hence the undefined.
Try this instead:
var find = function(list, predicate) { // Functional style
var ret;
_.each(list, function(elem){
if (!ret && predicate(elem)) {
return ret = elem;
}
});
return ret;
};

Array objects difference javascript angularjs

I have 2 array objects and I want to get the difference between them as follows:
array1 = [{"name":"MPCC","id":"tool:mpcc"}, {"name":"APP","id":"tool:app"}, {"name":"AII","id":"tool:aii"}, {"name":"VZZ","id":"tool:vzz"}, {"name":"USU","id":"tool:usu"}]
array2 = [{"name":"APP","id":"tool:app"}, {"name":"USU","id":"tool:usu"}]
result = [{"name":"MPCC","id":"tool:mpcc"}, {"name":"AII","id":"tool:aii"}, {"name":"VZZ","id":"tool:vzz"}]
Here is the code:
$scope.initial = function(base, userData){
var result = [];
angular.forEach( base, function(baseItem) {
angular.forEach( userData, function( userItem ) {
if ( baseItem.id !== userItem.id ) {
if (result.indexOf(baseItem) < 0) {
result.push(baseItem);
}
}
});
});
return result;
}
$scope.initial(array1, array2);
The problem with the above code is I dont get the desired result. Please let me know what is going wrong.
That is not related to Angular.
You could do something like this:
var result = array1.filter(function(item1) {
for (var i in array2) {
if (item1.id === array2[i].id) { return false; }
};
return true;
});
Or with ES6 syntax:
var result = array1.filter(i1 => !array2.some(i2 => i1.id === i2.id));
I think this is not related to Angular itself. You're looking for an algorithm to compute a difference between 2 sets.
The topic has already been discussed. You may also be interested on this underscore plugin

_.each function for looping in JS issue

We are asked to do the following:
Write a function called checkValue that searches an array for a value. It takes an array and a value and returns true if the value exists in the array, otherwise it returns false.
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function(arr, val) {
//checks if the val is in arr
}
Rewrite checkValue using _.each.
here is what I have to itterate over helloArr using _.each:
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function (num) {
return num;
}
checkValue('hola');
var output = us.each(helloArr, function(num){
if (checkValue())
{return true;}});
return output;
What am I doing wrong? When I run it with node, theres no errors but no output either. I know you can use _.find to do this but the spec is asking to itterate over the array and find the value using _.each.
In your second example, you're calling checkValue without a parameter, so it's going to return undefined, which is a falsey value, and the callback to each never returns anything.
Then again, it doesn't normally need to return anything anyway. _.each returns the list it operates on.
_.each is like a for-loop; consider treating it more like one.
function checkValue_original1(arr, val) {
for (var i = 0; i < arr.length; i++) {
if (val == arr[i]) return true;
}
return false;
}
function checkValue_original2(arr, val) {
return arr.indexOf(val) >= 0;
}
function checkValue_us_each(arr, val) {
var found = false;
_.each(arr, function(element, index, list) {
if (element == val) found = true;
});
return found;
}

List data structures in JavaScript

In an exercise in the book Eloquent JavaScript I need to create a list data structure (as below) based on the array [1, 2, 3].
The tutorial JavaScript Data Structures - The Linked List shows how to do this, but I don't really understand the intention to create this.start and this.end variables inside the tutorial.
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
I tried to solve this via the code below.
function arrayToList(array){
var list = { value:null, rest:null};
for(i=0; i<array.length-1; i++)
list.value = array[i];
list.rest = list;
return list;
}
This code gives me an infinite loop of array[0]. What's wrong with my code?
This tutorial shows how to do this but I don't really understand the intention to create this.start and this.end variables inside the tutorial.
The tutorial uses a List wrapper around that recursive structure with some helper methods. It says: "It is possible to avoid having to record the end of the list by performing a traverse of the entire list each time you need to access the end - but in most cases storing a reference to the end of the list is more economical."
This code gives me an infinite loop of array[0].
Not really, but it creates a circular reference with the line list.rest = list;. Probably the code that is outputting your list chokes on that.
What's wrong is with my code?
You need to create multiple objects, define the object literal inside the loop body instead of assigning to the very same object over and over! Also, you should access array[i] inside the loop instead of array[0] only:
function arrayToList(array){
var list = null;
for (var i=array.length-1; i>=0; i--)
list = {value: array[i], rest:list};
return list;
}
This particular data structure is more commonly called cons. Recursion is the most natural (not necessarily the most efficient) way to work with conses. First, let's define some helper functions (using LISP notation rather than "value/rest"):
function cons(car, cdr) { return [car, cdr] }
function car(a) { return a[0] }
function cdr(a) { return a[1] }
Now, to build a cons from an array, use the following recursive statement:
cons-from-array = cons [ first element, cons-from-array [ the rest ] ]
In Javascript:
function arrayToList(array) {
if(!array.length)
return null;
return cons(array[0], arrayToList(array.slice(1)));
}
And the reverse function is similarly trivial:
function listToArray(list) {
if(!list)
return [];
return [car(list)].concat(listToArray(cdr(list)));
}
function arrayToList (arr) {
var list = null;
for (var i = arr.length - 1; i >= 0; i--) {
list = {
value: arr[i],
rest: list
};
}
return list;
}
function prepend (elem, list) {
return {
value: elem,
rest: list
};
}
function listToArray (list) {
var arr = [];
for (var node = list; node; node = node.rest) {
arr.push(node.value);
}
return arr;
}
function nth(list, num) {
if (!list) {
return undefined;
} else if (num === 0) {
return list.value;
} else {
return nth(list.rest, num - 1);
}
}

How can I extend Array.prototype.push()?

I'm trying to extend the Array.push method so that using push will trigger a callback method and then perform the normal array function.
I'm not quite sure how to do this, but here's some code I've been playing with unsuccessfully.
arr = [];
arr.push = function(data){
//callback method goes here
this = Array.push(data);
return this.length;
}
arr.push('test');
Since push allows more than one element to be pushed, I use the arguments variable below to let the real push method have all arguments.
This solution only affects the arr variable:
arr.push = function () {
//Do what you want here...
return Array.prototype.push.apply(this, arguments);
}
This solution affects all arrays. I do not recommend that you do that.
Array.prototype.push = (function() {
var original = Array.prototype.push;
return function() {
//Do what you want here.
return original.apply(this, arguments);
};
})();
First you need subclass Array:
ES6 (https://kangax.github.io/compat-table/es6/):
class SortedArray extends Array {
constructor(...args) {
super(...args);
}
push() {
return super.push(arguments);
}
}
ES5 (proto is almost deprecated, but it is the only solution for now):
function SortedArray() {
var arr = [];
arr.push.apply(arr, arguments);
arr.__proto__ = SortedArray.prototype;
return arr;
}
SortedArray.prototype = Object.create(Array.prototype);
SortedArray.prototype.push = function() {
this.arr.push(arguments);
};
Array.prototype.push was introduced in JavaScript 1.2. It is really as simple as this:
Array.prototype.push = function() {
for( var i = 0, l = arguments.length; i < l; i++ ) this[this.length] = arguments[i];
return this.length;
};
You could always add something in the front of that.
You could do it this way:
arr = []
arr.push = function(data) {
alert(data); //callback
return Array.prototype.push.call(this, data);
}
If you're in a situation without call, you could also go for this solution:
arr.push = function(data) {
alert(data); //callback
//While unlikely, someone may be using "psh" to store something important
//So we save it.
var saved = this.psh;
this.psh = Array.prototype.push;
var ret = this.psh(data);
this.psh = saved;
return ret;
}
While I'm telling you how to do it, you might be better served with using a different method that performs the callback and then just calls push on the array rather than overriding push. You may end up with some unexpected side effects. For instance, push appears to be varadic (takes a variable number of arguments, like printf), and using the above would break that.
You'd need to do mess with _Arguments() and _ArgumentsLength() to properly override this function. I highly suggest against this route.
Or you could use "arguments", and that'd work too. I still advise against taking this route though.
There's another, more native method to achieve this: Proxy
const target = [];
const handler = {
set: function(array, index, value) {
// Call callback function here
// The default behavior to store the value
array[index] = value;
// Indicate success
return true;
}
};
const proxyArray = new Proxy(target, handler);
I wanted to call a function after the object has been pushed to the array, so I did the following:
myArray.push = function() {
Array.prototype.push.apply(this, arguments);
myFunction();
return myArray.length;
};
function myFunction() {
for (var i = 0; i < myArray.length; i++) {
//doSomething;
}
}

Categories

Resources