How can I change the index order of an array? - javascript
I have a button that has a function called clickNext(). Whenever that button is clicked, it increments the index position (scope.selected) on an array called 'arr1'.
<button type="button" class="right-btn col-xs-6" role="menuitem" ng-click="clickNext()">Next</button>
.
function clickNext()
{
scope.selected = (scope.selected + 1) % length;
}
arr1 = [
{apple: 1 , tango},
{banana: 3, kappa},
{orange:5, alpha},
{apple: 8 , beta},
{grape: 10 , sigma}
]
Problem
I have an identical array to arr1 called 'arr2'. What I'm trying to do is have the clickNext() increment to the next index position based on the arr2 array instead of the arr1 array.
Right now, the clickNext function still increments in the order of the arr1 array. For example, if I were to click the button, it would start on orange:5 then move to apple 8, etc.
arr2 = [
{orange:5, alpha},
{apple: 8 , beta},
{banana: 3, kappa},
{grape: 10 , sigma},
{apple: 1 , tango}
]
What I have tried
My though process to accomplish this is to use the findIndex() function and match the arr2 item to the arr1 item. That doesn't work, but maybe I'm structuring it wrong?
clickNext(){
var oldIndex = filteredToXs(scope.selected);
scope.selected = oldIndex + 1;}
function filteredToXs( filteredIndex ) {
var firstArr = scope.arr1[ filteredIndex ];
var xsIndex = scope.arr2.findIndex( function(item) {
return item.trackingNumber === firstArr.trackingNumber;
} );
if( xsIndex >= 0 ) return xsIndex;
if( xsIndex === -1 ) return 0; // Default value
}
I hope I understood your question correctly. Please read my comments in the code sections as well.
I had to modify your source so I was able to create a fiddle for you.
HTML: I changed the click event and removed a css class that's not available
<button type="button" role="menuitem" onclick="clickNext();">Next</button>
Sampe Arrays:
They were containing invalid objects: I changed alpha, beta, tango, .. to a property. You can also define them as values.. this shouldn't matter:
var arr1 = [
{ apple: 1, tango: '' },
{ banana: 3, kappa: '' },
{ orange: 5, alpha: '' },
{ apple: 8, beta: '' },
{ grape: 10, sigma: '' }];
var arr2 = [
{ orange: 5, alpha: '' },
{ apple: 8, beta: '' },
{ banana: 3, kappa: '' },
{ grape: 10, sigma: '' },
{ apple: 1, tango: '' }];
Code:
var idx = 0; //save current index of array 2
function clickNext() {
idx++;
//I'm comparing the array objects using a string compare- this only works because you said 'I have an identical array'
//this may cause issues if you're objects are cross-referenced
var find = function(array, obj) { //lookup value from arr2 in arr1
for (var i=0, l=array.length;i<l;i++)
if (JSON.stringify(array[i]) == JSON.stringify(obj)) //adjust this line to your needs
return array[i];
}
var result = find(arr1, arr2[idx])
if (!result)
throw new Error('not found- your arrays are not identical or you run out of index');
console.log(result);
}
fiddle: https://jsfiddle.net/k50y8pp5/
Related
Could not perform a recursive multidimensional array
I am making a recursive multidimensional array in javascript. But with a matrix I find it difficult. For example, when I do this: matrix([2,3,4]) I want it to return this to me: [ [ [ 0, 1, 2, 3 ] , [ 0, 1, 2, 3 ] , [ 0, 1, 2, 3 ] ] , [ [ 0, 1, 2, 3 ] , [ 0, 1, 2, 3 ] , [ 0, 1, 2, 3 ] ] ] The length of the entered matrix must be the number of dimensions and the numbers must be the value of the dimensions, having a 3D 2x3x4 matrix (height, width and height). Code: function copyArray(A) { var B=[] for(var i=0;i<A.length;i++) { B[i]=A[i] } return B } function matrix(dims) { var I=dims[0] dims.shift() var A=[] A.length=I for(var i=0;i<I;i++) { var dims2=copyArray(dims) A[i]=matriz(dims) dims=dims2 } return A } The code I have generates the following error: Uncaught RangeError: Invalid array length(…)
You can do it this way, but it should be mentioned first: Array(length): to create an array of the specified length. .shift(): to remove the first element from the array. dims.length ?: to see if the recursive function should still be executed. dims.slice(0): to clone the array passed to the function. function matrix(dims) { var arr = Array(dims.shift() || 0); for(var idx = 0; idx < arr.length; idx++) { arr[idx] = dims.length ? matrix(dims.slice(0)) : idx; } return arr; } console.log( matrix([2,3,4]) ) .as-console-wrapper {max-height: 100%!important;top:0}
Use a recursive function for this... build the level according to the dimension remove the index of the level you have dealt and move foward... do this until there no more dimensions to be handled. This is an example of how to do it... const createRange = (FROM, TO) => [...Array(TO - FROM + 1).keys()].map(i => i + FROM); const createMatrix = (dimensions) => { const dim = dimensions[0]; const newDimensions = dimensions.slice(1, dimensions.length); if (!newDimensions.length) return createRange(0, dim - 1); return [...Array(dim).keys()] .map(_ => createMatrix(newDimensions)); }; console.log( createMatrix([2,3,4]) )
Another approach using Array.from() and it's built in mapper const matrix = (dims) => ( Array.from({length: dims.shift()}, (_,i) => dims.length ? matrix([...dims]) : i) ) console.log(matrix ([2,3,4]))
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: Split an array according to a pattern: items 1, 5, 10, then 2, 6, 11, then 3, 7, 12
I am trying to split an array which has a repeating pattern of elements 1, 2, 3, and 4. I want to turn my array [1,2,3,4,5,6,7,8,9,10] into four arrays: [1,5,10], [2,6,11], [3,7,12], and [4,8,13]. I tried using multiples, but the result creates the new arrays in a wrong order. Here is my attempt: var upload_names_and_ids = [ "Certificat de salaire", //first line is the upload's visible title "certificat-de-salaire", //second line is the upload's id "no-info-circle", //third line is the info-circle class "", //fourth line is the info-circle text "Allocations Familiales", "alloc-familiales", "no-info-circle", "", "Courrier Impot (déclaration précédente)", "courrier-impot", "info-circle right", "" ]; //Seperate our first array into 4 var upload_names = []; var upload_ids = []; var upload_info_circle_class = []; var upload_info_circle_content = []; for (var i=0; i<upload_names_and_ids.length; i++){ if (i%4==0) { upload_info_circle_content.push(upload_names_and_ids[i]); } else if (i%3==0) { upload_info_circle_class.push(upload_names_and_ids[i]); } else if (i%2==0) { upload_names.push(upload_names_and_ids[i]); } else { upload_ids.push(upload_names_and_ids[i]); } } Any help is much appreciated, thank you!
You could take a remainder with index and wanted length. const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], length = 4, result = array.reduce( (r, v, i) => (r[i % length].push(v), r), Array.from({ length }, _ => []) ); console.log(result); If you like to use predeclared array directly, you could replace this line Array.from({ length }, _ => []) with [upload_names, upload_ids, upload_info_circle_class, upload_info_circle_content] where the accumulator of Array#reduce keeps the object references.
It's not i%3==0 (which matches 0, 3, 6, …) but i%4==1 (to match 1, 5, 10, …). Same for i%2==0.
I would add a helper sliceN that takes an array and a positive integer. Then returns an array of arrays where the inner arrays are of length n. sliceN([1,2,3,4,5,6,7,8,9], 3) //=> [[1,2,3], [4,5,6], [7,8,9]] sliceN([1,2,3,4,5,6], 2) //=> [[1,2], [3,4], [5,6]] Then also add a helper transpose that transposes a matrix. transpose([[1,2,3], [4,5,6], [7,8,9]]) //=> [[1,4,7], [2,5,8], [3,6,9]] transpose([[1,2], [3,4], [5,6]]) //=> [[1,3,5], [2,4,6]] With these two helpers you can create the wanted result with ease. const upload_names_and_ids = [ "Certificat de salaire", //first line is the upload's visible title "certificat-de-salaire", //second line is the upload's id "no-info-circle", //third line is the info-circle class "", //fourth line is the info-circle text "Allocations Familiales", "alloc-familiales", "no-info-circle", "", "Courrier Impot (déclaration précédente)", "courrier-impot", "info-circle right", "" ]; const [ upload_names, upload_ids, upload_info_circle_class, upload_info_circle_content, ] = transpose(sliceN(upload_names_and_ids, 4)); console.log(upload_names); console.log(upload_ids); console.log(upload_info_circle_class); console.log(upload_info_circle_content); function sliceN(array, n) { const slices = []; for (let i = 0; i < array.length; i += n) { slices.push(array.slice(i, i + n)); } return slices; } function transpose(rows) { if (rows.length == 0) return []; const columns = rows[0].map(cell => Array.of(cell)); for (let iRow = 1; iRow < rows.length; iRow += 1) { for (let iCol = 0; iCol < columns.length; iCol += 1) { columns[iCol].push(rows[iRow][iCol]); } } return columns; } If you are already use a library with helper functions chances are that one or both of these data transforming methods are present. sliceN can often be found as something with split, slice or chunk in the name. transpose is very specific and if present will probably be present under the same name. As an example Ramda offers both these methods. R.transpose(R.splitEvery(4, upload_names_and_ids))
Using array reduce to reduce two properties of an array of objects
I want to store all duplicates and unique values of suits and values of an array of Card objects. Each card has a suit and value property. The datastructure looks like this: cards = [ { suit: 'spades', value : 4 }, { suit: 'hearts', value : 4 }, { suit: 'spades', value : 11 }, { suit: 'spades', value : 12 } etc... ] I'm trying to use array.reduce to check and store duplicates and unique values and suits for both, but am having trouble structuring the code. Rules: 3-of-a-kind with different suits 4-card-run (with incrementing values) with same suits Basically... I need to check each card's value and suits... and count the duplicates and uniques of values and suits. I'm struggling passing in an array of objects and using reduce on it. Output: something like melds : { values: [4, 4, 4] suits: [spades, hearts, diamonds] }, runs : { values: [11, 12, 13], suits: ['spades', 'spades', 'spades'] } Code: function calculate(cards) { var my_array = cards.reduce(function(prev_array, curr, index, array){ if (prev_array.duplicates.values.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) { prev_array.duplicates.values.push(curr); } else { prev_array.uniques.values.push(curr); } if (prev_array.duplicates.suits.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) { prev_array.uniques.suits.push(curr); } else { prev_array.duplicates.suits.push(curr); } return prev_array; }, { duplicates : { values : [], suits : [] }, uniques : { values : [], suits : [] } } ); return my_array; } Edit: var values = [2, 3, 4, 5, 6, 6, 6]; var suits = ['spades', 'spades', 'spades', 'spades', 'diamonds', 'clubs', 'hearts']; var test_values = potentialRunsAndMelds(values); var test_suits = potentialRunsAndMelds(suits); function potentialRunsAndMelds(array) { var my_array = array.reduce(function(prev_array, curr, index, array){ if (prev_array.duplicates.indexOf(curr) !== -1 || array.lastIndexOf(curr) !== index) { prev_array.duplicates.push(curr); } else { prev_array.uniques.push(curr); } return prev_array; }, { uniques : [], duplicates : [] } ); return my_array; } var values = [2, 3, 4, 5, 6, 6, 6]; var suits = ['spades', 'hearts', 'spades', 'spades', 'diamonds', 'clubs', 'spades']; EDIT 2: var runs = Object.keys(groups.suits).map(function (suit) { var values = groups.suits[suit].sort(); var run = []; for (var i = 0; i < values.length; i++) { if (values[i+1] - values[i] === 1) { if (run.indexOf(values[i+1]) === -1) { run.push(values[i+1]); } if (run.indexOf(values[i]) === -1) { run.push(values[i]); } } } if (run.length >= 4) return run; }); Returns : [Array[4], undefined, undefined, undefined] Where Array[4] is [2, 3, 4, 5] How can I not return undefined? I suppose I can just do: runs = runs.filter(function (run) { return run.length; });
You might be trying to do too much in one reduce function; maybe try breaking this into steps? Anyway, having your reducer group by value and suit would simplify things. var groups = cards.reduce(function (accumulator, card) { // Group by value. accumulator.values[card.value] = accumulator.values[card.value] || []; accumulator.values[card.value].push(card.suit); // Group by suit. accumulator.suits[card.suit] = accumulator.suits[card.suit] || []; accumulator.suits[card.suit].push(card.value); return accumulator; }, {values: {}, suits: {}}); Once you've done that, it's much easier to find melds and runs. // Melds var meldValues = Object.keys(groups.values).filter(function (value) { // Check for duplicates in this array, if so inclined. return groups.values[value].length >= 3; }); // Runs var runs = Object.keys(groups.suits).map(function (suit) { var values = groups.suits[suit].sort(); // (1) iterate over values // (2) append each value to current 'run' as long as it's consecutive // (3) if not consecutive, start a new run. if duplicate, discard. // (4) return all runs with length >= 4 });
OK, I don't gamble and maybe I quite don't understood what you want to achieve, but it seem to me, that you forgot to get property value from cards array. You are passing the curr if (prev_array.duplicates.values.indexOf(curr) !== -1 ... But curr is card object. cards = [ { suit: 'spades', value : 4 }, You should target suit and value like curr.suit and cur.value. And because in JS, Objects cannot be easily compared so lastIndexOf(object) === indexOf(object) equals in all cases. and you need to check uniques Array if the value is'nt already there, because you cannot rely on lastIndexOf(). Also, because Objects cannot be easily compared, testing duplicity with Object is bad idea.
JavaScript: Indexing an object by column values
I have a table array which looks like this: tablearray = [ {'column1': 1, 'column2': 1, 'column3': 1, 'column4': 2}, {'column1': 1, 'column2': 2, 'column3': 3, 'column4': 4}, {'column1': 2, 'column2': 0, 'column3': 4, 'column4': 6} ] I'm trying to make a function which takes the table array and an array of column names and makes a new object indexed by the column values. So newObject = indexByColumnValues(tablearray, ['column1', 'column2']); should result in an object like newObject = { 1: { 1: {'column1': 1, 'column2': 1, 'column3': 1, 'column4': 2}, 2: {'column1': 1, 'column2': 2, 'column3': 3, 'column4': 4} } 2: { 0: {'column1': 2, 'column2': 0, 'column3': 4, 'column4': 6} } } So newObject[1][1]['column3'] = 1 newObject[1][2]['column4'] = 4 etc... If the number of columns in the column name array (['column1', 'column2'] above) is known, the solution is not hard. But if I allow for any number of column names in this array, it becomes more difficult as there is indefinite recursion newObject[tablearray[columnNameArray[0]][tablearray[columnNameArray[1]][tablearray[columnNameArray[2]]... Here is one attempt. I tried to use a pointer to point to the dimensional depth of the newObject array. First, pointer = newObject. Then pointer = newObject[...[0]]. Then point = newObject[...[0]][...[1]]. And so on. This builds the object properly but then I do not have a way to assign a value to newObject[...[0]]...[...[k]]. function indexByColumnValues(object, columnNameArray) { var newObject = {}; for(i in object) { var index=[]; for(j in columnNameArray) { index.push(object[i][columnNameArray[j]]); } var pointer = newObject; for(j in index) { if(pointer[index[j]] == undefined) { pointer[index[j]] = {}; } pointer = pointer[index[j]]; } //now pointer points to newObject[index[0]][index[1]]...[index[k]] //but I need to set newObject[...] above to be object[i]. How? //pointer = object[i]; //won't work } return newObject; } Any help or hints would be great here. Thanks.
You mention recursion, but you don't use it in your code. This is a classic situation where recursion is the right tool. Here's one implementation: function indexByColumnValues(table, cols) { // get the column we're indexing var col = cols[0], index = {}, x, val; // find all values for (x=0; x<table.length; x++) { val = table[x][col]; // add to index if necessary if (!index[val]) index[val] = []; // push this row index[val].push(table[x]); } // recurse if necessary if (cols.length > 1) { for (x in index) { if (index.hasOwnProperty(x)) { // pass the filtered table and the next column index[x] = indexByColumnValues( index[x], cols.slice(1) ); } } } return index; } Note that, as #jfriend00 notes, you want the "leaf" of your index to be an array of matching rows, not a single object - it's just coincidence that in your example you only have one matching row for your given data and set of columns. Usage: indexByColumnValues(tablearray, ['column1','column2']); Output: { "1":{ "1":[ {"column1":1,"column2":1,"column3":1,"column4":2} ], "2":[ {"column1":1,"column2":2,"column3":3,"column4":4} ] }, "2":{ "0":[ {"column1":2,"column2":0,"column3":4,"column4":6} ] } } JsFiddle: http://jsfiddle.net/RRcRM/3/