What function acts as .SelectMany() in jQuery? - javascript

Let me explain more:
we know that map function in jQuery acts as .Select() (as in LINQ).
$("tr").map(function() { return $(this).children().first(); }); // returns 20 tds
now the question is how can we have .SelectMany() in jQuery?
$("tr").map(function() { return $(this).children(); }); // returns 10 arrays not 20 tds!
here is my example in action: http://jsfiddle.net/8aLFQ/4/
"l2" should be 8 if we have selectMany.
[NOTE] please don't stick to this example, above code is to just show what I mean by SelectMany() otherwise it's very easy to say $("tr").children();
Hope it's clear enough.

map will flatten native arrays. Therefore, you can write:
$("tr").map(function() { return $(this).children().get(); })
You need to call .get() to return a native array rather than a jQuery object.
This will work on regular objects as well.
var nested = [ [1], [2], [3] ];
var flattened = $(nested).map(function() { return this; });
flattened will equal [1, 2, 3].

You want this:
$("tr").map(function() { return $(this).children().get(); });
Live demo: http://jsfiddle.net/8aLFQ/12/

You're going to kick yourself:
$("tr").map(function() { return [ $(this).children() ]; });
It's the simple things in life you treasure.
-- Fred Kwan
EDIT:
Wow, that will teach me to not to test answers thoroughly.
The manual says that map flattens arrays, so I assumed that it would flatten an array-like object. Nope, you have to explicit convert it, like so:
$("tr").map(function() { return $.makeArray($(this).children()); });
Things should be as simple as possible, but no simpler. -- Albert Einstein

$.map expects a value (or an array of values) returned. The jQuery object you are returning is being used as a "value" instead of an "array" (which get flattened)
All you need to do is return the array of DOM elements. jQuery provides a .get() method that returns a plain array from a selection.
$("tr").map(function() { return $(this).children().get() });
Of course, I understand this is a very contrived example, since $("tr").children() does the same thing with a lot less function calls.
http://jsfiddle.net/gnarf/8aLFQ/13/

I had the same question for regular arrays, and this is the only reference I could find in StackOverflow, so I'll add the answer I came up with.
For regular arrays, you can use
Array.prototype.selectMany = function (selector) {
return this.map(selector).reduce(function (a, b) {
return a.concat(b);
});
};
Thus [[1, 2], [3, 4], [5, 6, 7]].selectMany(function (a) { return a; }) evaluates to [1, 2, 3, 4, 5, 6, 7].
To use this in jQuery, you have to convert your jQuery set into an array before using it:
var result = $("tr").get().selectMany(function(a) {
return Array.prototype.slice.call(a.childNodes);
});

Not sure about .selectMany() but you could change the position of .children to get the desired result.
var l2 = $("tr").children().map(function() { return $(this); }).length;
http://jsfiddle.net/8aLFQ/5/
EDIT
I think I better understand what you're after following the comments.
You can call $.makeArray(l2) to return what you are after... that is 8 objects/arrays
http://jsfiddle.net/8aLFQ/10/

Related

Removing repeated values from array with multiple arguments

These are the full instructions for what I am trying to accomplish :
You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
This is the solution I have:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.slice(0,1);
return arr.filter(function(elements) {
return args.indexOf(element) === -1;
});
}
Keep in mind that there could be any number of arguments (not just 2 or 3).
The solution I have isn't working. What is wrong with my current solution and how can I fix it with an explanation?
Your solution is almost working.
The problems:
args.slice(0,1); does not modify the array (anyway this method just returns an array with the first element). Use args.shift() instead to remove the first element
function(elements) should be function(element) in the filter callback.
A working solution:
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.shift();
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
console.log(destroyer([1, 2, 3, 5, 5], 1, 5)); // prints [2, 3]
Check the working demo.

Recursively make one level array from multi levels array in JavaScript

I'm confused a little, please help me anyone.
This is what I have
var arr = [
1,
2,
[11, 12, 13],
3,
[11, 12,
[21, 22]
]
];
As you can see, it's multi-lever array. Using recursion I need to make a one level array from it, which contains all elements from its children. Should be like this:
[1,2,11,12,13,3,11,12,21,22]
Here's how I'm doing it:
function getAllNumbers(arr) {
var allNumbers = [];
arr.forEach(function (el) {
if (Array.isArray(el)) {
allNumbers.concat(getAllNumbers(el))
} else {
allNumbers.push(el)
}
});
return allNumbers;
}
and it's not working. Gives me back only first level, like this: [1,2,3]
I'm particularly interested in recursion so please don't try to find other way.
please help
P.S. I can easily find sum of all elements with similar method just a little modified, but cannot do this :(
Almost everything was fine :)
just
The concat() method returns a new array comprised of the array on which it is called joined with the array(s) and/or value(s) provided as arguments.
so, instead of
allNumbers.concat(getAllNumbers(el))
should be
allNumbers = allNumbers.concat(getAllNumbers(el))
and it works :)

Check if one array is contained in another array

I have
['a', 'b', 'c']
I want to know if this array is contained in this array:
['a', 'b', 'c', 'd']
I know I can do 2 for loops and check item per item, but is there a oneliner for it?
You can do this using Array.prototype.some. This will run the provided function against all the items in an array, and return true if the function returns true for any of them. The following will return true if any items from array are not contained in otherArray, which you can use to determine if one array is fully contained in the other:
return !array.some(function(item) {
return otherArray.indexOf(item) === -1;
});
However, this is not the most elegant solution. The logic can be summed up as:
not any items from array not in other array
Which has far too many negatives. We can instead use Array.prototype.every, which is very similar except it returns true only if all items in an array return true for the provided function. The below is equivalent to what we had before:
return array.every(function(item) {
return otherArray.indexOf(item) !== -1;
});
Except that can be summed up as:
all items in array in other array
Finally, we can implement this as an additional prototype function. Note that the second parameter for every is optional, and sets what this refers to in the function when provided. If we did not pass it in, we would not be able to refer to the this from the outside scope.
Array.prototype.contains = function(array) {
return array.every(function(item) {
return this.indexOf(item) !== -1;
}, this);
}
This can now be used as a one liner in our code:
['a', 'b', 'c'].contains(['a', 'b']) // returns true
If you are able to use ECMAScipt 6, you can use arrow functions to make this a true one-liner.
return array.every(item => otherArray.indexOf(item) !== -1);
ES6 one-lined answer
containedArray.every(element => mainArray.includes(element))
...an improved answer on top of the ES6 suggestion of #James Brierley:
by using every(...) (which returns true if all the elements pass the test we've provided - false otherwise) alongside includes, which IMO is more human-readable - and less error prone - than checking for a index !== -1.
var mainArray = [1, 30, 39, 29, 10, 13];
var containedArray = [1, 30, 39, 29]
console.log(containedArray.every(element => mainArray.includes(element)));

Javascript: Why array variable assignment is behaving differently inside and outside this function?

For the life of me, I just can't figure out what I'm doing wrong here.
I'm trying to use both the reduce and concat array methods to take all of the values of a 2d array and combine them into a single value (basically condense them into a single array and then sum them up).
The problem that I keep running into is that when I try to make a for/loop to concat each array element, the argument that I'm passing into the function is not being recognized as an array, thus my call to .concat() is failing. I've placed a console.log() at the beginning of the function to see if the element is being recognized as the first array element in the 2d array, and it's coming up as "1"(?).
I tried another test outside of the function, and it logs as the actual array element. What am I doing wrong here? code below:
var arrays = [[1, 2, 3], [4, 5], [6]];
var myArray = arrays[0]; // Test
console.log(myArray); // Test
var flatArray = arrays.reduce(function(arrays)
{
console.log(arrays[0]); // Test
for (var i = 0; i < arrays.length - 1; i++)
{
arrays[0].concat(arrays[i+1]);
}
return arrays;
});
console.log(flatArray);
This is the output that I keep getting:
Array [ 1, 2, 3 ]
1
TypeError: arrays[0].concat is not a function
It's almost seems like array is being converted to a number-type when inside the function...?
You have an error in your code here:
var flatArray = arrays.reduce(function(param) {})
that param will be an element of your arrays vector.
Check this https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
You are using .reduce() incorrectly and you don't even need to use it to flatten an array. You can just do this:
var flatArray = [].concat.apply([],arrays);
Working demo: http://jsfiddle.net/jfriend00/wfjyfp42/
To understand .reduce(), the callback you pass it gets four arguments (see MDN reference). The first two arguments are important in using .reduce() correctly:
callback(previousValue, currentValue, index, array)
The previousValue is the accumulated value so far in the reduction. The currentValue is the next element of the array that is being iterated. The last two arguments do not need to be used if not needed.
Your code is only using the previousValue so it is never looking at the next item in the array as passed in by .reduce().
You could make a solution work using .reduce() like this:
var flatArray = arrays.reduce(function(previousValue, currentValue) {
return previousValue.concat(currentValue);
}, []);
Working demo: http://jsfiddle.net/jfriend00/2doohfc5/
Reduce performs an operation on two elements.
var sum = [[1, 2, 3], [4, 5], [6]].reduce(function(a, b) {
return a.concat(b);
}).reduce(function(a, b) {
return a + b;
});

A Javascript function named method

I found an implementation using using prototypes. This is a simplification to show the structure:
function Thingie(){
this.content = [];
}
Thingie.prototype = {
push: function(arg) {
this.content.push(arg);
},
pop: function() {
return this.content.pop();
}
};
var t = new Thingie();
forEach([10, 3, 4, 8, 2, 9, 7, 1, 2, 6, 5],
method(t, "push"));
What is "method" in the example on the last line? I've never seen this construct. I use t.push like everyone else.
I tried find how "method()" is defined online, but it is impossible to search for a function called "method" using any possible set of search terms. All you get are how functions and methods are defined and used. There also seems to be no information when I look at forEach documentation.
Does this make sense to anybody?
method(t, "push")
would be defined as:
function method(obj, name) {
return obj[name].bind(obj);
}
That forEach looks like UnderscoreJS's function _.each
_.each(list, iteratee, [context]) Alias: forEach
example:
_.each([1,2,3], function(item) { console.log(item); });
// console output:
// 1
// 2
// 3
That method probably looks like this (Beware: wild guess!) , giving you the function to use as iteratee paramater
function method(obj, name){
if(typeof(obj[name]) != "function")
throw new Error("Not a function");
return obj[name];
}
A function like that is lodash.bindKey, which does exactly what you want. Also, the forEach method could be lodash.forEach, or simply Array.prototype.forEach, which is built-in.
[1,2,3].forEach(_.bindKey(t, 'push'));
However, this works because Thinghie#push expects only one argument. If the same call would be made on an array, the result would not be as expected, since forEach methods take 3 arguments: value, index, array, and [].push can handle multiple arguments. So, the code
var array = [];
[1,2].forEach(_.bindKey(array, 'push'));
console.log(array); // outputs [1, 0, [1, 2], 2, 1, [1, 2]]
In this case (and in any case when we would like the function returned by method applied on only one argument), I guess the solution is to write
function method(obj, name) {
return function(arg) { return obj[name](arg); }
}

Categories