How to create a partition function in javascript. using the following guidelines - javascript
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]]
Related
Why is this Array.prototype.reduce method not working
function dropElements(arr, func) { let output = arr.reduce((acc=[], elem) => { if (func(elem)){ acc.push(arr.slice(arr.indexOf(elem))) return acc } }, []) return output } let tester = dropElements([1, 2, 3, 4,5,6,3,2,1], function(n) {return n >= 3;}) console.log(tester) I want it to output [3,4,5,6,3,2,1]. But is printing copies of size decreasing arrays. The statement of the problem: "Given the array arr, iterate through and remove each element starting from the first element (the 0 index) until the function func returns true when the iterated element is passed through it."
You can use Array#slice along with Array#findIndex to find the first index at which the callback function returns true. function dropElements(arr, func) { const idx = arr.findIndex(func); return idx >= 0 ? arr.slice(idx) : []; } console.log(dropElements([1, 2, 3, 4, 5, 6, 3, 2, 1], n => n >= 3)); If you specifically want to use Array#reduce, you could use a variable to store whether or not the callback has returned true for any element so far. function dropElements(arr, func) { let found = false; return arr.reduce((acc, elem) => { found = found || func(elem); if (found) acc.push(elem); return acc; }, []); } console.log(dropElements([1, 2, 3, 4, 5, 6, 3, 2, 1], n => n >= 3));
Use the shift method to remove the first element from the array until the array is empty or func(arr[0]) returns a truthy value. This method is inefficient as it could be, but it matches the problem statement. Particularly: It removes each element singularly, rather than all at once. It removes elements while iterating. It operates on the array itself, rather than a copy. function dropElements(arr, func) { while (arr.length > 0 && !func(arr[0])) { arr.shift(); } return arr; } let tester = dropElements([1, 2, 3, 4,5,6,3,2,1], function(n) {return n >= 3;}); console.log(tester);
How to remove an empty array index [duplicate]
How do I remove empty elements from an array in JavaScript? Is there a straightforward way, or do I need to loop through it and remove them manually?
A few simple ways: var arr = [1,2,,3,,-3,null,,0,,undefined,4,,4,,5,,6,,,,]; arr.filter(n => n) // [1, 2, 3, -3, 4, 4, 5, 6] arr.filter(Number) // [1, 2, 3, -3, 4, 4, 5, 6] arr.filter(Boolean) // [1, 2, 3, -3, 4, 4, 5, 6] or - (only for single array items of type "text") ['','1','2',3,,'4',,undefined,,,'5'].join('').split(''); // output: ["1","2","3","4","5"] or - Classic way: simple iteration var arr = [1,2,null, undefined,3,,3,,,0,,,[],,{},,5,,6,,,,], len = arr.length, i; for(i = 0; i < len; i++ ) arr[i] && arr.push(arr[i]); // copy non-empty values to the end of the array arr.splice(0 , len); // cut the array and leave only the non-empty values // [1,2,3,3,[],Object{},5,6] jQuery: var arr = [1,2,,3,,3,,,0,,,4,,4,,5,,6,,,,]; arr = $.grep(arr, n => n == 0 || n); // [1, 2, 3, 3, 0, 4, 4, 5, 6]
EDIT: This question was answered almost nine years ago when there were not many useful built-in methods in the Array.prototype. Now, certainly, I would recommend you to use the filter method. Take in mind that this method will return you a new array with the elements that pass the criteria of the callback function you provide to it. For example, if you want to remove null or undefined values: var array = [0, 1, null, 2, "", 3, undefined, 3,,,,,, 4,, 4,, 5,, 6,,,,]; var filtered = array.filter(function (el) { return el != null; }); console.log(filtered); It will depend on what you consider to be "empty" for example, if you were dealing with strings, the above function wouldn't remove elements that are an empty string. One typical pattern that I see often used is to remove elements that are falsy, which include an empty string "", 0, NaN, null, undefined, and false. You can pass to the filter method, the Boolean constructor function, or return the same element in the filter criteria function, for example: var filtered = array.filter(Boolean); Or var filtered = array.filter(function(el) { return el; }); In both ways, this works because the filter method in the first case, calls the Boolean constructor as a function, converting the value, and in the second case, the filter method internally turns the return value of the callback implicitly to Boolean. If you are working with sparse arrays, and you are trying to get rid of the "holes", you can use the filter method passing a callback that returns true, for example: var sparseArray = [0, , , 1, , , , , 2, , , , 3], cleanArray = sparseArray.filter(function () { return true }); console.log(cleanArray); // [ 0, 1, 2, 3 ] Old answer: Don't do this! I use this method, extending the native Array prototype: Array.prototype.clean = function(deleteValue) { for (var i = 0; i < this.length; i++) { if (this[i] == deleteValue) { this.splice(i, 1); i--; } } return this; }; test = new Array("", "One", "Two", "", "Three", "", "Four").clean(""); test2 = [1, 2,, 3,, 3,,,,,, 4,, 4,, 5,, 6,,,,]; test2.clean(undefined); Or you can simply push the existing elements into other array: // Will remove all falsy values: undefined, null, 0, false, NaN and "" (empty string) function cleanArray(actual) { var newArray = new Array(); for (var i = 0; i < actual.length; i++) { if (actual[i]) { newArray.push(actual[i]); } } return newArray; } cleanArray([1, 2,, 3,, 3,,,,,, 4,, 4,, 5,, 6,,,,]);
If you need to remove ALL empty values ("", null, undefined and 0): arr = arr.filter(function(e){return e}); To remove empty values and Line breaks: arr = arr.filter(function(e){ return e.replace(/(\r\n|\n|\r)/gm,"")}); Example: arr = ["hello",0,"",null,undefined,1,100," "] arr.filter(function(e){return e}); Return: ["hello", 1, 100, " "] UPDATE (based on Alnitak's comment) In some situations you may want to keep "0" in the array and remove anything else (null, undefined and ""), this is one way: arr.filter(function(e){ return e === 0 || e }); Return: ["hello", 0, 1, 100, " "]
Simply one liner: [1, false, "", undefined, 2].filter(Boolean); // [1, 2] or using underscorejs.org: _.filter([1, false, "", undefined, 2], Boolean); // [1, 2] // or even: _.compact([1, false, "", undefined, 2]); // [1, 2]
If you've got Javascript 1.6 or later you can use Array.filter using a trivial return true callback function, e.g.: arr = arr.filter(function() { return true; }); since .filter automatically skips missing elements in the original array. The MDN page linked above also contains a nice error-checking version of filter that can be used in JavaScript interpreters that don't support the official version. Note that this will not remove null entries nor entries with an explicit undefined value, but the OP specifically requested "missing" entries.
For removing holes, you should use arr.filter(() => true) arr.flat(0) // New in ES2019 For removing hole, null, and, undefined: arr.filter(x => x != null) For removing hole, and, falsy (null, undefined, 0, -0, 0n, NaN, "", false, document.all) values: arr.filter(x => x) arr = [, null, (void 0), 0, -0, 0n, NaN, false, '', 42]; console.log(arr.filter(() => true)); // [null, (void 0), 0, -0, 0n, NaN, false, '', 42] console.log(arr.filter(x => x != null)); // [0, -0, 0n, NaN, false, "", 42] console.log(arr.filter(x => x)); // [42] Note: Holes are some array indexes without elements. arr = [, ,]; console.log(arr[0], 0 in arr, arr.length); // undefined, false, 2; arr[0] is a hole arr[42] = 42; console.log(arr[10], 10 in arr, arr.length); // undefined, false, 43; arr[10] is a hole arr1 = [1, 2, 3]; arr1[0] = (void 0); console.log(arr1[0], 0 in arr1); // undefined, true; a[0] is undefined, not a hole arr2 = [1, 2, 3]; delete arr2[0]; // NEVER do this please console.log(arr2[0], 0 in arr2, arr2.length); // undefined, false; a[0] is a hole All above methods are returning a copy of the given array, not modifying it in-place. arr = [1, 3, null, 4]; filtered = arr.filter(x => x != null); console.log(filtered); // [1, 3, 4] console.log(arr); // [1, 3, null, 4]; not modified
The clean way to do it. var arr = [0,1,2,"Thomas","false",false,true,null,3,4,undefined,5,"end"]; arr = arr.filter(Boolean); // [1, 2, "Thomas", "false", true, 3, 4, 5, "end"]
Actually, you can use ES6+ methods, assume the array is below: const arr = [1,2,3,undefined,4,5,6,undefined,7,8,undefined,undefined,0,9]; And the answer could be one of these two ways: First way: const clearArray = arr.filter(i => i); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] Second way: const clearArray = arr.filter(Boolean); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] Update 14th Oct 2022: Those two answers aren't utterly correct, even in the given example, yeah, it works but pay attention to the number 0 in the given array, by both ways number zero is disappeared and it's obviously related to checking items by using boolean coercion. A completely correct way is to check nulish and remove them: const notNil = (i) => !(typeof i === 'undefined' || i === null); const clearArray = arr.filter(i => isNil(i)); const arr = [1,2,3,undefined,4,5,6,undefined,7,8,undefined,undefined,0,9]; const notNil = (i) => !(typeof i === 'undefined' || i === null); console.log("Not nil: ", arr.filter(notNil));
ES6: let newArr = arr.filter(e => e);
Simple ES6 ['a','b','',,,'w','b'].filter(v => v);
With Underscore/Lodash: General use case: _.without(array, emptyVal, otherEmptyVal); _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); With empties: _.without(['foo', 'bar', '', 'baz', '', '', 'foobar'], ''); --> ["foo", "bar", "baz", "foobar"] See lodash documentation for without.
If using a library is an option I know underscore.js has a function called compact() http://documentcloud.github.com/underscore/ it also has several other useful functions related to arrays and collections. Here is an excerpt from their documentation: _.compact(array) Returns a copy of the array with all falsy values removed. In JavaScript, false, null, 0, "", undefined and NaN are all falsy. _.compact([0, 1, false, 2, '', 3]); => [1, 2, 3]
#Alnitak Actually Array.filter works on all browsers if you add some extra code. See below. var array = ["","one",0,"",null,0,1,2,4,"two"]; function isempty(x){ if(x!=="") return true; } var res = array.filter(isempty); document.writeln(res.toJSONString()); // gives: ["one",0,null,0,1,2,4,"two"] This is the code you need to add for IE, but filter and Functional programmingis worth is imo. //This prototype is provided by the Mozilla foundation and //is distributed under the MIT license. //http://www.ibiblio.org/pub/Linux/LICENSES/mit.license if (!Array.prototype.filter) { Array.prototype.filter = function(fun /*, thisp*/) { var len = this.length; if (typeof fun != "function") throw new TypeError(); var res = new Array(); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) { var val = this[i]; // in case fun mutates this if (fun.call(thisp, val, i, this)) res.push(val); } } return res; }; }
Since nobody else mentioned it and most people have underscore included in their project you can also use _.without(array, *values);. _.without(["text", "string", null, null, null, "text"], null) // => ["text", "string", "text"]
To remove undefined elements from an array you can simply use const array = [ { name: "tim", age: 1 }, undefined, { name: "ewrfer", age: 22 }, { name: "3tf5gh", age: 56 }, null, { name: "kygm", age: 19 }, undefined, ]; console.log(array.filter(Boolean));
You may find it easier to loop over your array and build a new array out of the items you want to keep from the array than by trying to loop and splice as has been suggested, since modifying the length of the array while it is being looped over can introduce problems. You could do something like this: function removeFalsyElementsFromArray(someArray) { var newArray = []; for(var index = 0; index < someArray.length; index++) { if(someArray[index]) { newArray.push(someArray[index]); } } return newArray; } Actually here is a more generic solution: function removeElementsFromArray(someArray, filter) { var newArray = []; for(var index = 0; index < someArray.length; index++) { if(filter(someArray[index]) == false) { newArray.push(someArray[index]); } } return newArray; } // then provide one or more filter functions that will // filter out the elements based on some condition: function isNullOrUndefined(item) { return (item == null || typeof(item) == "undefined"); } // then call the function like this: var myArray = [1,2,,3,,3,,,,,,4,,4,,5,,6,,,,]; var results = removeElementsFromArray(myArray, isNullOrUndefined); // results == [1,2,3,3,4,4,5,6] You get the idea - you could then have other types of filter functions. Probably more than you need, but I was feeling generous... ;)
What about this(ES6) : To remove Falsy value from an array. var arr = [0,1,2,"test","false",false,true,null,3,4,undefined,5,"end"]; arr.filter((v) => (!!(v)==true)); //output: //[1, 2, "test", "false", true, 3, 4, 5, "end"]
You should use filter to get array without empty elements. Example on ES6 const array = [1, 32, 2, undefined, 3]; const newArray = array.filter(arr => arr);
foo = [0, 1, 2, "", , false, 3, "four", null] foo.filter(e => e === 0 ? true : e) returns [0, 1, 2, 3, "four"] If you're positive you won't have any 0's in your array, it can look a bit nicer: foo.filter(e => e)
When using the highest voted answer above, first example, i was getting individual characters for string lengths greater than 1. Below is my solution for that problem. var stringObject = ["", "some string yay", "", "", "Other string yay"]; stringObject = stringObject.filter(function(n){ return n.length > 0}); Instead of not returning if undefined, we return if length is greater than 0. Hope that helps somebody out there. Returns ["some string yay", "Other string yay"]
I'm simply adding my voice to the above “call ES5's Array..filter() with a global constructor” golf-hack, but I suggest using Object instead of String, Boolean, or Number as suggested above. Specifically, ES5's filter() already doesn't trigger for undefined elements within the array; so a function that universally returns true, which returns all elements filter() hits, will necessarily only return non-undefined elements: > [1,,5,6,772,5,24,5,'abc',function(){},1,5,,3].filter(function(){return true}) [1, 5, 6, 772, 5, 24, 5, 'abc', function (){}, 1, 5, 3] However, writing out ...(function(){return true;}) is longer than writing ...(Object); and the return-value of the Object constructor will be, under any circumstances, some sort of object. Unlike the primitive-boxing-constructors suggested above, no possible object-value is falsey, and thus in a boolean setting, Object is a short-hand for function(){return true}. > [1,,5,6,772,5,24,5,'abc',function(){},1,5,,3].filter(Object) [1, 5, 6, 772, 5, 24, 5, 'abc', function (){}, 1, 5, 3]
var data = [null, 1,2,3]; var r = data.filter(function(i){ return i != null; }) console.log(r) [1,2,3]
Removing all empty elements If an array contains empty Objects, Arrays, and Strings alongside other empty elements, we can remove them with: const arr = [ [], ['not', 'empty'], {}, { key: 'value' }, 0, 1, null, 2, "", "here", " ", 3, undefined, 3, , , , , , 4, , 4, , 5, , 6, , , ] let filtered = JSON.stringify( arr.filter((obj) => { return ![null, undefined, ''].includes(obj) }).filter((el) => { return typeof el != "object" || Object.keys(el).length > 0 }) ) console.log(JSON.parse(filtered)) Simple compacting (removing empty elements from an array) With ES6: const arr = [0, 1, null, 2, "", 3, undefined, 3, , , , , , 4, , 4, , 5, , 6, , , ,] let filtered = arr.filter((obj) => { return ![null, undefined].includes(obj) }) console.log(filtered) With plain Javascript -> var arr = [0, 1, null, 2, "", 3, undefined, 3, , , , , , 4, , 4, , 5, , 6, , , ,] var filtered = arr.filter(function (obj) { return ![null, undefined].includes(obj) }) console.log(filtered)
You can use filter with index and in operator let a = [1,,2,,,3]; let b = a.filter((x,i)=> i in a); console.log({a,b});
What about that: js> [1,2,,3,,3,,,0,,,4,,4,,5,,6,,,,].filter(String).join(',') 1,2,3,3,0,4,4,5,6
Another way to do it is to take advantage of the length property of the array : pack the non-null items on the 'left' of the array, then reduce the length. It is an in-place algorithm -does not allocates memory, too bad for the garbage collector-, and it has very good best/average/worst case behaviour. This solution, compared to others here, is between 2 to 50 times faster on Chrome, and 5 to 50 times faster on Firefox, as you might see here : http://jsperf.com/remove-null-items-from-array The code below adds the non-enumerable 'removeNull' method to the Array, which returns 'this' for daisy-chaining : var removeNull = function() { var nullCount = 0 ; var length = this.length ; for (var i=0, len=this.length; i<len; i++) { if (!this[i]) {nullCount++} } // no item is null if (!nullCount) { return this} // all items are null if (nullCount == length) { this.length = 0; return this } // mix of null // non-null var idest=0, isrc=length-1; length -= nullCount ; while (true) { // find a non null (source) slot on the right while (!this[isrc]) { isrc--; nullCount--; } if (!nullCount) { break } // break if found all null // find one null slot on the left (destination) while ( this[idest]) { idest++ } // perform copy this[idest]=this[isrc]; if (!(--nullCount)) {break} idest++; isrc --; } this.length=length; return this; }; Object.defineProperty(Array.prototype, 'removeNull', { value : removeNull, writable : true, configurable : true } ) ;
None of the answers above works best for all types. The below solution will remove null, undefined, {} [], NaN and will preserve date string and what's best is it removes even from nested objects. function removeNil(obj) { // recursively remove null and undefined from nested object too. return JSON.parse(JSON.stringify(obj), (k,v) => { if(v === null || v === '') return undefined; // convert date string to date. if (typeof v === "string" && /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\d\d\dZ$/.test(v)) return new Date(v); // remove empty array and object. if(typeof v === 'object' && !Object.keys(v).length) return undefined; return v; }); } function removeNil(obj) { // recursively remove null and undefined from nested object too. return JSON.parse(JSON.stringify(obj), (k,v) => { if(v === null || v === '') return undefined; // convert date string to date. if (typeof v === "string" && /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\d\d\dZ$/.test(v)) return new Date(v); // remove empty array and object. if(typeof v === 'object' && !Object.keys(v).length) return undefined; return v; }); } const ob = { s: 'a', b: 43, countries: [ 'a', 'b', 'c' ], l: null, n: { ks: 'a', efe: null, ce: '' }, d: new Date(), nan: NaN, k: undefined, emptyO: {}, emptyArr: [], } const output = removeNil(ob); console.log(output); console.log('Tests: ', ob.countries.length, typeof(ob.d))
This works, I tested it in AppJet (you can copy-paste the code on its IDE and press "reload" to see it work, don't need to create an account) /* appjet:version 0.1 */ function Joes_remove(someArray) { var newArray = []; var element; for( element in someArray){ if(someArray[element]!=undefined ) { newArray.push(someArray[element]); } } return newArray; } var myArray2 = [1,2,,3,,3,,,0,,,4,,4,,5,,6,,,,]; print("Original array:", myArray2); print("Clenased array:", Joes_remove(myArray2) ); /* Returns: [1,2,3,3,0,4,4,5,6] */
'Misusing' the for ... in (object-member) loop. => Only truthy values appear in the body of the loop. // --- Example ---------- var field = []; field[0] = 'One'; field[1] = 1; field[3] = true; field[5] = 43.68; field[7] = 'theLastElement'; // --- Example ---------- var originalLength; // Store the length of the array. originalLength = field.length; for (var i in field) { // Attach the truthy values upon the end of the array. field.push(field[i]); } // Delete the original range within the array so that // only the new elements are preserved. field.splice(0, originalLength);
This might help you : https://lodash.com/docs/4.17.4#remove var details = [ { reference: 'ref-1', description: 'desc-1', price: 1 }, { reference: '', description: '', price: '' }, { reference: 'ref-2', description: 'desc-2', price: 200 }, { reference: 'ref-3', description: 'desc-3', price: 3 }, { reference: '', description: '', price: '' } ]; scope.removeEmptyDetails(details); expect(details.length).toEqual(3); scope.removeEmptyDetails = function(details){ _.remove(details, function(detail){ return (_.isEmpty(detail.reference) && _.isEmpty(detail.description) && _.isEmpty(detail.price)); }); };
Javascript fixing my flatten(array) function
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];
Changing rows to columns javascript
I wanted to change the rows into columns of an array. [ [1], [1,2], [1,2,3], [4,2,3], [4,5,3], [4,5,6] ] to [ [1,1,1,4,4,4], [2,2,2,5,5], [3,3,3,6] ] I tried var res = []; for(i in this.fields) { for(j in this.fields[i].value) { if(i === 0) res[j] = []; res[j][i] = this.fields[i].value[j]; } } this gives me empty set.
Create this function: function transpose(arr) { return Object.keys(arr[0]).map(function (c) { return arr.map(function (r) { return r[c]; }); }); } and then: var transposedArray = transpose(originalArray);
What you're asking looks a little weird because you have different lengths and you're ignoring undefined values, but it is still achievable. Don't use for..in loops for Array, use a normal for. Also, you'll need to know how many items you'll have in your new parent Array, which is the max of the lengths of the original child Arrays. var arrR = [ // will refer to "down" and "across" as in this literal [1], [1, 2], [1, 2, 3], [4, 2, 3], [4, 5, 3], [4, 5, 6] ]; function r2c(arr) { var arrC = [], // next get the longest sub-array length x = Math.max.apply(Math, arr.map(function (e) {return e.length;})), y = arr.length, i, j; for (i = 0; i < x; ++i) { // this is the loop "down" arrC[i] = []; for (j = 0; j < y; ++j) // and this is the loop "across" if (i in arr[j]) arrC[i].push(arr[j][i]); } return arrC; } var arrC = r2c(arrR); /* [ [1, 1, 1, 4, 4, 4], [2, 2, 2, 5, 5], [3, 3, 3, 6] ] */ You should still consider if you're happy with [[1], [1, 2], [1]] becoming [[1, 1, 1], [2]], which I would consider unexpected (the position of 2 is completely lost), but seems to be what you intend.
Similar to Pauls but doesn't need to get the max length first: function transpose(arr) { // Loop over arrays as long as one has values // Arrays should be contiguous, may fail if sparse for (var result = [], i=0, more; more; i++) { more = false; // Get the ith element of each array (if there is one) for (var j=0, jLen=arr.length; j<jLen; j++) { // Don't add missing members if (arr[j].hasOwnProperty(i)) { // Add array for result if not already there result[i] = result[i] || []; // Do transpose result[i][j] = arr[j][i]; // Only keep going while there is data more = true; } } } return result; } BTW, a fixed version of your original function is: function transpose2(fields) { // Make sure the result array is initialised var res = []; // Don't forget to keep counters local - declare them // I've removed *this* as it's a plain function, use it if // it's an instance method for(var i in fields) { // Values are read directly, there is no "value" accessor for(var j in fields[i]) { // Don't rely on order of enumeration - may not start at 0 if(!res[j]) res[j] = []; // Do the transpose res[j][i] = fields[i][j]; } } return res; } But as noted above, for..in is not liked for arrays, particularly as there are many libraries that extend built-ins like Array.prototype so you will traverse those properties too. But if you're cool with that, this is a good way to deal with sparse arrays. You can add a hasOwnProperty test to avoid inherited enumerables. Note also that the order of enumeration isn't necessarily from '0' or in any particular order, hence changed way of initialising res[j].
Flatten an array of arrays in javascript for get the longest string
I am trying to flatten an array of arrays in a input, and return the longest string. For example given the input: i = ['big',[0,1,2,3,4],'tiny'] The function should return 'tiny'. I would like use reduce or concat for resolve this in a native and elegant way (without implement a flatten prototype in array) but I am failing with this code: function longestStr(i) { // It will be an array like (['big',[0,1,2,3,4],'tiny']) // and the function should return the longest string in the array // This should flatten an array of arrays var r = i.reduce(function(a, b) { return a.concat(b); }); // This should fetch the longest in the flattened array return r.reduce(function (a, b) { return a.length > b.length ? a : b; }); }
Your issue is that you forgot to pass in the initialValue argument to the reduce function, which must be an array in this case. var r = i.reduce(function(a, b) { return a.concat(b); }, []); Without providing an initialValue, the a value for the first call will be the first element in the i array, which is the string big in your case, so you will be calling the String.prototype.concat function instead of Array.prototype.concat. That means at the end, r is a string and strings don't have a reduce function. Your solution could be simplified however: ['big',[0,1,2,3],'tiny'].reduce(function longest(a, b) { b = Array.isArray(b)? b.reduce(longest, '') : b; return b.length > a.length? b : a; }, '');
You don't mention having multiple strings with the same length- or if you care about IE8... function longestStr(A){ var i= 0, len, A= String(A).split(/\b/).sort(function(a, b){ return a.length<b.length; }); len= A[0].length; while(A[i].length==len)++i; return A.slice(0, i); } var A1= ['big', [0, 1, 2, 3, 4], 'tiny',[1,2,3,'puny']]; longestStr(A1); /* returned value: (Array) tiny,puny */ Method 2: You did not define a string as a single word- any array delimeters could be included in any of the values, making my solution incorrect. Flattening the array makes comparing each item's length simple- and it does not have to be done as a prototype method: function longestStr(array){ function flatten(arr){ var A1= [], L= arr.length, next; for(var i= 0; i<L; i++){ next= arr[i]; if(next.constructor!= Array) A1.push(String(next)); else A1= A1.concat(flatten(next)); } return A1; } var i= 0, len, A=flatten(array); A.sort(function(a, b){ return a.length<b.length; }); len= A[0].length; while(A[i].length== len)++i; return A.slice(0, i); } var Ax= ['big stuff', [0, 1, 2, 3, 4], 'tiny', [1, 2, 3, 'puny']]; longestStr(Ax); /* returned value: (Array) big stuff */