Javascript fixing my flatten(array) function - javascript

I can not figure out why my code does not flatten out the nested arrays as indicated. I'd greatly appreciate some help here. I used a recursion to get to the actual value of the nested array. I tried to debug my code, and it seems to replace my results array every time the recursion takes place.
//Helper methods
function toType(obj){
return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
function each(collection, callback){
if (Array.isArray(collection)){
for (var i = 0; i < collection.length; i++){
callback(collection[i], i, collection)
}
} else {
for (var i in collection){
callback(collection[i], i, collection)
}
}
}
//Flatten function
function flatten(array, isShallow=false, callback){
var results = [];
each(array, function(item){
if (!!isShallow && toType(item) === 'array'){
each (item, function(value){
results.push(value);
})
} else if (toType(item) !== 'array'){
results.push(item);
} else {
return flatten(item)
}
})
return results;
}
flatten([1, [2], [3, [[4]]]]);
// ---> [1]

Your problem appears to be with this line:
return flatten(item)
Returning here is a problem because the loop will end and the current entries in results will be ignored, as well as the remaining items. The line should be changed to instead append the results of
flatten(item)
to results array via push.
I recommend using a library for this sort of thing. http://underscorejs.org/#flatten is a great one!

Please see the refactored code below.
The major change is that instead of creating new copies of results, we are passing it to subsequent calls to flatten as a reference.
Please see the added comments
//Helper methods
function toType(obj){
return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
function each(collection, callback){
if (Array.isArray(collection)){
for (var i = 0; i < collection.length; i++){
callback(collection[i], i, collection)
}
} else if(typeof collection === 'object'){
//type number was failing here
for (var i in collection){
callback(collection[i], i, collection)
}
}
else {
//default for primitive types
callback(collection, 0, collection);
}
}
//Flatten function
//Removed isShallow, how do we know if its shallow or not?
//Added results as arg, so we only manipulate the reference to results
//And to not create multiple scopes of var results;
function flatten(array, results, callback){
results = results || [];
each(array, function(item){
//removed 3rd if clause not needed.
//Only need to know if item is an object or array
if (toType(item) === 'array' || toType(item) === 'object'){
each (item, function(value){
flatten(value,results);
})
} else {
results.push(item);
}
})
return results;
}
var array1 = [1,[2,[3,4]]];
var array2 = [5,[6,[7,[8, {a:9,b:[10,11,12]}]]]];
var obj = {a:array1, b:array2};
console.log(flatten(array1)); // [ 1, 2, 3, 4 ]
console.log(flatten(array2)); // [ 5, 6, 7, 8, 9, 10, 11, 12 ]
console.log(flatten(obj)); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]

You can do something like that:
function flatten(array, i) {
i = i || 0;
if(i >= array.length)
return array;
if(Array.isArray(array[i])) {
return flatten(array.slice(0,i)
.concat(array[i], array.slice(i+1)), i);
}
return flatten(array, i+1);
}
Example:
var weirdArray = [[],1,2,3,[4,5,6,[7,8,9,[10,11,[12,[[[[[13],[[[[14]]]]]]]]]]]]]
flatten(weirdArray);
//returns ==> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

The best way to flatten you array in javascript is to use splice function of Array. Please follow the following code. It works for nesting as well.
function flattenArray(arr){
for(var i=0;i<arr.length;i++){
if(arr[i] instanceof Array){
Array.prototype.splice.apply(arr,[i,1].concat(arr[i]))
}
}
return arr;
}

Use underscore.js's flatten function (http://underscorejs.org/#flatten). Underscore.js is a 3rd party library with 80 some-odd functions to make your life as a javascript programmer easier. Don't reinvent the wheel.
var _ = require('underscore');
_.flatten([1, [2], [3, [[4]]]]);
=> [1, 2, 3, 4];

Related

Library Function to have Unique Items (remove duplicated) Array is not running

I am trying to make a "Library Function" to remove duplicated array entries. I have written following snippet but it doesn't seem to work. Can anyone help fix it?
var arr1 = [5, 4, 2, 6, 9, 2, 8, 1, 6];
Array.prototype.unique = function(arr){
var result = [];
arr.forEach(item){
if(result.indexOf(item) === -1){
result.push(item);
}
}
return result;
}
console.log(arr1.unique());
I believe what you are attempting to do is the following. The small change I made was to use the this keyword to reference the array itself. Also the function needs to be inside the foreach(fn => {}).
Array.prototype.unique = function() {
var result = [];
this.forEach(function(item) {
if(result.indexOf(item) === -1) {
result.push(item);
}
})
return result;
}
Making a version that uses an arrow function inside the foreach loop and uses includes, so you have some options.
Array.prototype.unique = function() {
var result = [];
this.forEach(item => {
if (!result.includes(item)) {
result.push(item);
}
})
return result;
}
Maybe this will help you.
No need to pass an argument, you can access array using this
var arr1 = [5, 4, 2, 6, 9, 2, 8, 1, 6];
Array.prototype.unique = function() {
var result = [];
this.forEach((item) => {
if (result.indexOf(item) === -1) {
result.push(item);
}
});
return result;
}
console.log(arr1.unique());
In console.log() must be :
console.log(arr1.unique(arr1));
your unique function receives an array as a parameter
Easy way to do this, You can try this
let arr1 = [5, 4, 2, 6, 9, 2, 8, 1, 6];
let result= Array.from(new Set(arr1))
console.log(result)

Recursion to flatten an array not working

I was working on the steamroller problem of free code camp. I came up with two solutions: one works perfectly [1, 2, 3, 4], but the other is giving [ 4 ].
The problem asks to write a function that flatten any sort of array.
my working code:
function steamrollArray(arr) {
newArr = [];
for (i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) === true) {
newArr = newArr.concat(steamrollArray(arr[i]));
} else if (Array.isArray(arr[i]) === false) {
newArr.push(arr[i]);
}
}
return newArr;
}
steamrollArray([1, [2], [3, [[4]]]]);
my not working code:
function steamrollArray(arr) {
newArr = [];
for (i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) === true) {
newArr.push(steamrollArray(arr[i]));
} else if (Array.isArray(arr[i]) === false) {
newArr.push(arr[i]);
}
}
return newArr;
}
steamrollArray([1, [2], [3, [[4]]]]);
Can anyone explain why the second code give only [4] ?
That happens because in first code, you are using
newArr = newArr.concat() in which you are assigning it to local variable. So, even in recursion, the state will be stored.
But in second code, you are using newArr.push() and in recursion, you are again declaring newArr=[];
That is the issue.
function steamrollArray(arr) {
// I'm a steamroller, baby
let newArr = arr.slice(); //newArr is a copy of arr
var result = []; //this is going to be the flatted array
checkArray(newArr, result);
return result;
}
function checkArray(myArray, resultedArray) {
//this function is a a recursion
return myArray.map(elt => {
if (!Array.isArray(elt)) {
//so this is a simple element, it is not an array, lets push it
resultedArray.push(elt);
return elt;
} else {
return checkArray(elt, resultedArray);
}
});
}
console.log(steamrollArray([1, {},
[3, [
[4]
]]
])); // should return [1, {}, 3, 4].
Comments in the code are helpful

Filtering arguments from Arrays

The following code should take a given array and filter out all arguments that follow. For example the below code should return: [1, 1].
function destroyer(arr) {
var args= [];
args.push(arguments[0]);
var realArgs = args[0];
var filteredArr=[];
function removeIt (val){
return val != ;
}
filteredArr= realArgs.filter(removeIt);
return filteredArr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I can't figure out the filter function. Do I need to use Boolean somehow?
If you want to access the arguments of the function you should use arguments not args. The filtering part is not straightforward because javascript still doesn't have a builtin function we can use, so we have to implement it by ourselves (that's the includes function).
function destroyer(arr) {
var arr = arguments[0];
var toFilter = [];
for (var i = 0; i < arguments.length; i++)
toFilter.push(arguments[i]);
function removeIt (arr, numsToFilter){
var array = arr.slice(); // make sure to copy the array in order not to modify the original
for (var i = 0; i < array.length; i++) {
if (includes(numsToFilter, array[i])) {
delete array[i];
}
}
return array;
}
function includes(arr, k) {
for(var i=0; i < arr.length; i++){
if( arr[i] === k){
return true;
}
}
return false;
}
return removeIt(arr, toFilter);
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Running the code-example
You can call Array#slice on the arguments object to convert it to an array. We'll slice from the index 1 (skip the arr). Then you can Array#filter the arr array. On each iteration check if the item is in the toRemove array by using Array#indexOf. Items that are in toRemove will be filtered out. Note - Array#filter returns a new array.
function destroyer(arr) {
// convert all the arguments but arr into array
var toRemove = [].slice.call(arguments, 1);
// filter the original array
return arr.filter(function removeIt(val){
// keep all vals that are not in the toRemove array
return toRemove.indexOf(val) === -1;
});
}
var result = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
console.log(result);

freecodecamp Challenges- Seek and Destroy

I am trying to solve this challenge Seek and Destroy. I can't figure out what is wrong. Any help ?
Seek and Destroy
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 initial code below:
function destroyer(arr) {
// Remove all the values
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
This is my Code below:
function destroyer(arr) {
var letsDestroyThis = [];
var i =1 ; while (i<arguments.length) {
letsDestroyThis.push(arguments[i]);
i++;
}
for(var j=0 ; j< arguments[0].length; j++) {
for (var k= 0; k< letsDestroyThis.length; k++) {
if(arguments[0][j] === letsDestroyThis[k]){
arguments[0].splice(j, 1);
}
}
}
return arguments[0];
}
destroyer([2, 3, 2, 3], 2, 3);
Thanks in Advance!
You can create an array of all values that are supposed to be removed. Then use Array.filter to filter out these values.
Note: Array.splice will change original array.
function destroyer() {
var arr = arguments[0];
var params = [];
// Create array of all elements to be removed
for (var k = 1; k < arguments.length; k++)
params.push(arguments[k]);
// return all not matching values
return arr.filter(function(item) {
return params.indexOf(item) < 0;
});
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
function destroyer(arr) {
/* Put all arguments in an array using spread operator and remove elements
starting from 1 using slice intead of splice so as not to mutate the initial array */
const args = [...arguments].slice(1);
/* Check whether arguments include elements from an array and return all that
do not include(false) */
return arr.filter(el => !args.includes(el));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
This worked for me:
function destroyer(arr) {
// Remove all the values
var args = Array.from(arguments);
var filter = [];
for (i = 0; i < args[0].length; i++) {
for (j = 1; j < args.length; j++) {
if (args[0][i] === args[j]) {
delete args[0][i];
}
}
}
return args[0].filter(function(x) {
return Boolean(x);
});
}
console.log(
destroyer([1, 2, 3, 1, 2, 3], 2, 3)
);
//two ways of resolving the Seek and Destroy challenge on the FreeCodeCamp
//I was trying to simplify this code, please post your solutions, simplifying the code
//as much has its possible
function destroyer1 (arr){
//get array from arguments
var args = Array.prototype.slice.call(arguments);
args.splice(0,1);
for (var i = 0; i < arr.length; i++){
for(var j = 0; j < args.length; j++){
if(arr[i]===args[j]){
delete arr[i];
}
}
}
return arr.filter(function(value){
return Boolean(value);
});
}
//--------------------------------------
function destroyer(arr) {
// Remove all the values
//Get values from arguments of the function to an array, index 0(arr[0] will be "arr",
//rest of indexes will be rest of arguments.
var args = Array.from(arguments);
for (var i = 0 ; i < args[0].length; i++){
for (var j = 1; j < args.length; j++){
if(args[0][i] === args[j]){
delete args[0][i];
}
}
}
return args[0].filter(function(value){
return Boolean(value);
});
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
console.log(destroyer1([1,6,3,9,8,1,1], 3,1));
This is my Code:
function destroyer(arr) {
var argsBeRemove = [...arguments];
argsBeRemove.shift();
return arr.filter(val => {
return argsBeRemove.indexOf(val) == -1;
});
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
Here is my version of Seek and Destroy. I assume that there are no zero elements in input (that assumption allows to pass the challenge). But that way I can make found elements equal zero and then just filter them out. It is pretty straight forward and no index mess when deleting elements in for loops.
function destroyer(arr) {
// Remove all the values
var args = Array.prototype.slice.call(arguments);
var temp = [];
temp = arguments[0].slice();
for (j = 1; j < args.length; j++) {
for (i = 0; i < arguments[0].length; i++) {
if (arguments[0][i] == arguments[j]) {
temp[i] = 0;
}
}
}
function isZero(value) {
return value !== 0;
}
var filtered = temp.filter(isZero);
return filtered;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments, 1);
return arr.filter(destroyNum);
function destroyNum(element) {
return !args.includes(element);
}
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
Give this a shot, it is way less convoluted:
function destroyer(arr) {
// arr1 is equal to the array inside arr
var arr1 = arr.slice(arguments);
// arr2 becomes an array with the arguments 2 & 3
var arr2 = Array.prototype.slice.call(arguments, 1);
// this function compares the two and returns an array with elements not equal to the arguments
return arr1.concat(arr2).filter(function(item) {
return !arr1.includes(item) || !arr2.includes(item)
})
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
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.
The accepted solution returns a new array, instead of removing the elements from the existing array.
This can be achieved efficiently by iterating the array in reverse and removing any elements matching any of the filter arguments.
function destroy(original, ...matches) {
if('length' in original) {
let index = original.length
while(--index > -1) {
if(matches.includes(original[index])) {
original.splice(index, 1)
}
}
}
return original;
}
const original = [1, 2, 3, 1, 2, 3]
destroy(original, 2, 3)
console.log(original);
My answer is similar to previous one, but I didn't use indexOf. Instead of that I checked the values in cycle, but compiler gives me a warning to not to declare function in cycle.
function destroyer(arr) {
// Remove all the values
var temp = [];
for (var i = 1; i < arguments.length; i++) {
temp.push(arguments[i]);
arr = arguments[0].filter(function(value) {
return ( value !== temp[i - 1]) ;
});
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
We can get the arguments behind the array, which are the number required to be removed and store them to a list. Then, we can just use a filter to filter out the numbers that needed to be removed.
function destroyer(arr) {
let removeList=[...arguments].slice(1);
return arr.filter(e=>removeList.indexOf(e)===-1);
}
I know isn't the shortest way to do it, but i think is the more simple to understand in an intermediate level.
function destroyer(arr, ...elements) {
var i =0;
while(i<=arr.length){ //for each element into array
for(let j =0; j<elements.length; j++){ //foreach "elements"
if(arr[i]==elements[j]){ // Compare element arr==element
arr.splice(i,1); //If are equal delete from arr
i--; //Check again the same position
j=0;
break; //Stop for loop
}
}
i++;
}
return arr;
}
console.log(destroyer(["possum", "trollo", 12, "safari", "hotdog", 92, 65, "grandma", "bugati", "trojan", "yacht"], "yacht", "possum", "trollo", "safari", "hotdog", "grandma", "bugati", "trojan"));
console.log(destroyer([1, 2, 3, 5, 1, 2, 3], 2, 3));
function destroyer(arr) {
let newArray = Array.from(arguments).splice(1)
return arr.filter(item => !(newArray.includes(item)))
}

How to create a partition function in javascript. using the following guidelines

I've been trying to create a generic partition function that returns an array of arrays. the function should be made under the following guidelines:
Arguments:
An array
A function
Objectives:
Call <function> for each element in <array> passing it the arguments:
element, key, <array>
Return an array that is made up of 2 sub arrays:
0. An array that contains all the values for which <function> returned something truthy
1. An array that contains all the values for which <function> returned something falsy
Here is what I have so far. I get the return of two. I feel like maybe I just have to do the filter function on two separate occasions, but I'm not sure how to put it together. Thoughts and suggestions are highly appreciated.
_.partition = function (collection, test){
var allValues = [];
var matches = [];
var misMatches = [];
_.filter(collection.value, function(value, key, collection){
if (test(value[key], key, collection) === "string"){
matches.push(value[key]);
}else{
misMatches.push(value[key]);
}
});
return allValues.push(matches, misMatches);
}
Here is a version which uses reduce:
function partition(arr, filter) {
return arr.reduce(
(r, e, i, a) => {
r[filter(e, i, a) ? 0 : 1].push(e);
return r;
}, [[], []]);
}
Here's an alternative version which uses Array#filter to find the matches, and builds an array of non-matches as it goes along:
function partition(arr, filter) {
var fail = [];
var pass = arr.filter((e, i, a) => {
if (filter(e, i, a)) return true;
fail.push(e);
});
return [pass, fail];
}
You're correct about calling the filter method on separate occasions. One filter call would obtain the truthy values; the other would obtain the falsy values:
_.partition = function(collection, testFunc) {
var matches = collection.filter(function(elem) {
return test(elem) === 'string';
});
var misMatches = collection.filter(function(elem) {
return test(elem) !== 'string';
});
return [matches, misMatches];
}
You are close, but there are a couple issues I see:
You are returning the result of allValues.push which is not allValues itself, but rather the new length of the array.
You are using _.filter to iterate over array elements and sort them into two arrays. This is strange, since it's not the intended use of _.filter.
If you want a quick and readable solution using _.filter, this will work:
_.mixin({
partition: function(collection, test) {
return [
_.filter(collection, test), // items which satisfy condition
_.filter(collection, _.negate(test)) // items which don't
];
}
});
A more efficient solution which makes only one pass over the collection is below (this is almost what you already have):
_.mixin({
partition: function(collection, test) {
var matches = [], misMatches = [], value;
// can replace this loop with _.each
for (var i = 0, len = collection.length; i < len; ++i) {
value = collection[i];
// push the value into the appropriate array
if (test(value, i, collection)) {
matches.push(value);
} else {
misMatches.push(value);
}
}
return [matches, misMatches];
}
});
Usage examples (and Plunker):
function isOdd(x) {
return x % 2;
}
// _.mixin allows you to do either one of these
_.partition([1, 2, 3, 4, 5, 6], isOdd); // result: [[1, 3, 5], [2, 4, 6]]
_([1, 2, 3, 4, 5, 6]).partition(isOdd); // result: [[1, 3, 5], [2, 4, 6]]
// this is a use case you brought up in the comments
_.partition([1, "a", 2, "b", 3, "c"], _.isString); // result: [["a", "b", "c"], [1, 2, 3]]
This is generally known as partition ing in functional languages. You suply an array (xs) and a predicate function (p) to a reduceing function with initial value [[],[]].
var partition = (xs,p) => xs.reduce( (r,e) => ( p(e) ? r[0].push(e)
: r[1].push(e)
, r
)
, [[],[]]
);
Such that;
> partition([1,2,3,4,5,6,7,8,9,0], x => x < 5)
> [[1, 2, 3, 4, 0],[5, 6, 7, 8, 9]]

Categories

Resources