Convert JavaScript array of 2 element arrays into object key value pairs - javascript

What is the fastest algorithm for getting from something like this:
var array = [ [1,'a'], [2,'b'], [3,'c'] ];
to something like this:
Object { 1: "a", 2: "b", 3: "c" }
so far this is what i've come up with:
function objectify(array) {
var object = {};
array.forEach(function(element) {
object[element[0]] = element[1];
});
return object;
}
which works fine, but it seems kind of clumsy. Is there a better way? Would something like reduce() work and would that be any faster?

With Object.fromEntries, you can convert from Array to Object:
var array = [
[1, 'a'],
[2, 'b'],
[3, 'c']
];
var object = Object.fromEntries(array);
console.log(object);

You could indeed use Array.prototype.reduce:
function objectify(array) {
return array.reduce(function(p, c) {
p[c[0]] = c[1];
return p;
}, {});
}
where p is the result of the previous iteration, initially {}, and c is the current element of the array.
It's unlikely to be any faster than array.forEach, but it is IMHO cleaner. I don't believe there's any simpler implementation than this.
NB: a function to do exactly this already exists in the Underscore library: _.object(array)

Terse version using modern syntax:
let objectify = a => a.reduce( (o,[k,v]) => (o[k]=v,o), {} );
I use this technique as part of a terse query string parser:
// Converts "?foo=bar&j=1&go" into { foo:'bar', j:'1', go:true }
function parseQueryString(qs) {
var q = decodeURIComponent;
return qs.replace(/^\?/,'').split('&').map(s => s.split('='))
.reduce((o,[k,v]) => (o[q(k)] = v?q(v):true, o), {});
}

Lodash has a _.fromPairs method that does exactly that.
From the documentation:
_.fromPairs([['a', 1], ['b', 2]]);
// => { 'a': 1, 'b': 2 }

You can wrap the entire thing within Array.prototype.reduce, like this
function objectify(array) {
return array.reduce(function(result, currentArray) {
result[currentArray[0]] = currentArray[1];
return result;
}, {});
}
console.log(objectify([ [1, 'a'], [2, 'b'], [3, 'c'] ]));
# { '1': 'a', '2': 'b', '3': 'c' }
We are just accumulating the key-value pairs in the result object and finally the result of reduce will be the result object and we are returning it as the actual result.

Related

what is the shortest way to remove duplicate data/entries from an Array in Javascipt? [duplicate]

I have an array of numbers that I need to make sure are unique. I found the code snippet below on the internet and it works great until the array has a zero in it. I found this other script here on Stack Overflow that looks almost exactly like it, but it doesn't fail.
So for the sake of helping me learn, can someone help me determine where the prototype script is going wrong?
Array.prototype.getUnique = function() {
var o = {}, a = [], i, e;
for (i = 0; e = this[i]; i++) {o[e] = 1};
for (e in o) {a.push (e)};
return a;
}
More answers from duplicate question:
Remove duplicate values from JS array
Similar question:
Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array
With JavaScript 1.6 / ECMAScript 5 you can use the native filter method of an Array in the following way to get an array with unique values:
function onlyUnique(value, index, array) {
return self.indexOf(value) === index;
}
// usage example:
var a = ['a', 1, 'a', 2, '1'];
var unique = a.filter(onlyUnique);
console.log(unique); // ['a', 1, 2, '1']
The native method filter will loop through the array and leave only those entries that pass the given callback function onlyUnique.
onlyUnique checks, if the given value is the first occurring. If not, it must be a duplicate and will not be copied.
This solution works without any extra library like jQuery or prototype.js.
It works for arrays with mixed value types too.
For old Browsers (<ie9), that do not support the native methods filter and indexOf you can find work arounds in the MDN documentation for filter and indexOf.
If you want to keep the last occurrence of a value, simply replace indexOf with lastIndexOf.
With ES6 this can be shorten to:
// usage example:
var myArray = ['a', 1, 'a', 2, '1'];
var unique = myArray.filter((value, index, array) => array.indexOf(value) === index);
console.log(unique); // unique is ['a', 1, 2, '1']
Thanks to Camilo Martin for hint in comment.
ES6 has a native object Set to store unique values. To get an array with unique values you could now do this:
var myArray = ['a', 1, 'a', 2, '1'];
let unique = [...new Set(myArray)];
console.log(unique); // unique is ['a', 1, 2, '1']
The constructor of Set takes an iterable object, like an Array, and the spread operator ... transform the set back into an Array. Thanks to Lukas Liese for hint in comment.
Updated answer for ES6/ES2015: Using the Set and the spread operator (thanks le-m), the single line solution is:
let uniqueItems = [...new Set(items)]
Which returns
[4, 5, 6, 3, 2, 23, 1]
I split all answers to 4 possible solutions:
Use object { } to prevent duplicates
Use helper array [ ]
Use filter + indexOf
Bonus! ES6 Sets method.
Here's sample codes found in answers:
Use object { } to prevent duplicates
function uniqueArray1( ar ) {
var j = {};
ar.forEach( function(v) {
j[v+ '::' + typeof v] = v;
});
return Object.keys(j).map(function(v){
return j[v];
});
}
Use helper array [ ]
function uniqueArray2(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
a.push(arr[i]);
return a;
}
Use filter + indexOf
function uniqueArray3(a) {
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
// usage
var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']
return unique;
}
Use ES6 [...new Set(a)]
function uniqueArray4(a) {
return [...new Set(a)];
}
And I wondered which one is faster. I've made sample Google Sheet to test functions. Note: ECMA 6 is not avaliable in Google Sheets, so I can't test it.
Here's the result of tests:
I expected to see that code using object { } will win because it uses hash. So I'm glad that tests showed the best results for this algorithm in Chrome and IE. Thanks to #rab for the code.
Update 2020
Google Script enabled ES6 Engine. Now I tested the last code with Sets and it appeared faster than the object method.
You can also use underscore.js.
console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>
which will return:
[1, 2, 3, 4]
One Liner, Pure JavaScript
With ES6 syntax
list = list.filter((x, i, a) => a.indexOf(x) == i)
x --> item in array
i --> index of item
a --> array reference, (in this case "list")
With ES5 syntax
list = list.filter(function (x, i, a) {
return a.indexOf(x) == i;
});
Browser Compatibility: IE9+
Remove duplicates using Set.
Array with duplicates
const withDuplicates = [2, 2, 5, 5, 1, 1, 2, 2, 3, 3];
Get a new array without duplicates by using Set
const withoutDuplicates = Array.from(new Set(withDuplicates));
A shorter version
const withoutDuplicates = [...new Set(withDuplicates)];
Result: [2, 5, 1, 3]
Many of the answers here may not be useful to beginners. If de-duping an array is difficult, will they really know about the prototype chain, or even jQuery?
In modern browsers, a clean and simple solution is to store data in a Set, which is designed to be a list of unique values.
const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
const uniqueCars = Array.from(new Set(cars));
console.log(uniqueCars);
The Array.from is useful to convert the Set back to an Array so that you have easy access to all of the awesome methods (features) that arrays have. There are also other ways of doing the same thing. But you may not need Array.from at all, as Sets have plenty of useful features like forEach.
If you need to support old Internet Explorer, and thus cannot use Set, then a simple technique is to copy items over to a new array while checking beforehand if they are already in the new array.
// Create a list of cars, with duplicates.
var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
// Create a list of unique cars, to put a car in if we haven't already.
var uniqueCars = [];
// Go through each car, one at a time.
cars.forEach(function (car) {
// The code within the following block runs only if the
// current car does NOT exist in the uniqueCars list
// - a.k.a. prevent duplicates
if (uniqueCars.indexOf(car) === -1) {
// Since we now know we haven't seen this car before,
// copy it to the end of the uniqueCars list.
uniqueCars.push(car);
}
});
To make this instantly reusable, let's put it in a function.
function deduplicate(data) {
if (data.length > 0) {
var result = [];
data.forEach(function (elem) {
if (result.indexOf(elem) === -1) {
result.push(elem);
}
});
return result;
}
}
So to get rid of the duplicates, we would now do this.
var uniqueCars = deduplicate(cars);
The deduplicate(cars) part becomes the thing we named result when the function completes.
Just pass it the name of any array you like.
Using ES6 new Set
var array = [3,7,5,3,2,5,2,7];
var unique_array = [...new Set(array)];
console.log(unique_array); // output = [3,7,5,2]
Using For Loop
var array = [3,7,5,3,2,5,2,7];
for(var i=0;i<array.length;i++) {
for(var j=i+1;j<array.length;j++) {
if(array[i]===array[j]) {
array.splice(j,1);
}
}
}
console.log(array); // output = [3,7,5,2]
I have since found a nice method that uses jQuery
arr = $.grep(arr, function(v, k){
return $.inArray(v ,arr) === k;
});
Note: This code was pulled from Paul Irish's duck punching post - I forgot to give credit :P
Magic
a.filter(e=>!(t[e]=e in t))
O(n) performance - we assume your array is in a and t={}. Explanation here (+Jeppe impr.)
let unique = (a,t={}) => a.filter(e=>!(t[e]=e in t));
// "stand-alone" version working with global t:
// a1.filter((t={},e=>!(t[e]=e in t)));
// Test data
let a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];
let a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12]];
let a3 = ['Mike', 'Adam','Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];
// Results
console.log(JSON.stringify( unique(a1) ))
console.log(JSON.stringify( unique(a2) ))
console.log(JSON.stringify( unique(a3) ))
The simplest, and fastest (in Chrome) way of doing this:
Array.prototype.unique = function() {
var a = [];
for (var i=0, l=this.length; i<l; i++)
if (a.indexOf(this[i]) === -1)
a.push(this[i]);
return a;
}
Simply goes through every item in the array, tests if that item is already in the list, and if it's not, pushes to the array that gets returned.
According to JSBench, this function is the fastest of the ones I could find anywhere - feel free to add your own though.
The non-prototype version:
function uniques(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++)
if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
a.push(arr[i]);
return a;
}
Sorting
When also needing to sort the array, the following is the fastest:
Array.prototype.sortUnique = function() {
this.sort();
var last_i;
for (var i=0;i<this.length;i++)
if ((last_i = this.lastIndexOf(this[i])) !== i)
this.splice(i+1, last_i-i);
return this;
}
or non-prototype:
function sortUnique(arr) {
arr.sort();
var last_i;
for (var i=0;i<arr.length;i++)
if ((last_i = arr.lastIndexOf(arr[i])) !== i)
arr.splice(i+1, last_i-i);
return arr;
}
This is also faster than the above method in most non-Chrome browsers.
We can do this using ES6 sets:
var duplicatesArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];
var uniqueArray = [...new Set(duplicatesArray)];
console.log(uniqueArray); // [1,2,3,4,5]
["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) {
return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
}, []);
[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {
return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
}, []);
After looking into all the 90+ answers here, I saw there is room for one more:
Array.includes has a very handy second-parameter: "fromIndex", so by using it, every iteration of the filter callback method will search the array, starting from [current index] + 1 which guarantees not to include currently filtered item in the lookup and also saves time.
Note - this solution does not retain the order, as it removed duplicated items from left to right, but it wins the Set trick if the Array is a collection of Objects.
// 🚩 🚩 🚩
var list = [0,1,2,2,3,'a','b',4,5,2,'a']
console.log(
list.filter((v,i) => !list.includes(v,i+1))
)
// [0,1,3,"b",4,5,2,"a"]
Explanation:
For example, lets assume the filter function is currently iterating at index 2) and the value at that index happens to be 2. The section of the array that is then scanned for duplicates (includes method) is everything after index 2 (i+1):
πŸ‘‡ πŸ‘‡
[0, 1, 2, 2 ,3 ,'a', 'b', 4, 5, 2, 'a']
πŸ‘† |---------------------------|
And since the currently filtered item's value 2 is included in the rest of the array, it will be filtered out, because of the leading exclamation mark which negates the filter rule.
If order is important, use this method:
// 🚩 🚩 🚩
var list = [0,1,2,2,3,'a','b',4,5,2,'a']
console.log(
// Initialize with empty array and fill with non-duplicates
list.reduce((acc, v) => (!acc.includes(v) && acc.push(v), acc), [])
)
// [0,1,2,3,"a","b",4,5]
This has been answered a lot, but it didn't address my particular need.
Many answers are like this:
a.filter((item, pos, self) => self.indexOf(item) === pos);
But this doesn't work for arrays of complex objects.
Say we have an array like this:
const a = [
{ age: 4, name: 'fluffy' },
{ age: 5, name: 'spot' },
{ age: 2, name: 'fluffy' },
{ age: 3, name: 'toby' },
];
If we want the objects with unique names, we should use array.prototype.findIndex instead of array.prototype.indexOf:
a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);
This prototype getUnique is not totally correct, because if i have a Array like: ["1",1,2,3,4,1,"foo"] it will return ["1","2","3","4"] and "1" is string and 1 is a integer; they are different.
Here is a correct solution:
Array.prototype.unique = function(a){
return function(){ return this.filter(a) }
}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });
using:
var foo;
foo = ["1",1,2,3,4,1,"foo"];
foo.unique();
The above will produce ["1",2,3,4,1,"foo"].
You can simlply use the built-in functions Array.prototype.filter() and Array.prototype.indexOf()
array.filter((x, y) => array.indexOf(x) == y)
var arr = [1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 8, 9, 6, 9];
var newarr = arr.filter((x, y) => arr.indexOf(x) == y);
console.log(newarr);
[...new Set(duplicates)]
This is the simplest one and referenced from MDN Web Docs.
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]
Array.prototype.getUnique = function() {
var o = {}, a = []
for (var i = 0; i < this.length; i++) o[this[i]] = 1
for (var e in o) a.push(e)
return a
}
Without extending Array.prototype (it is said to be a bad practice) or using jquery/underscore, you can simply filter the array.
By keeping last occurrence:
function arrayLastUnique(array) {
return array.filter(function (a, b, c) {
// keeps last occurrence
return c.indexOf(a, b + 1) < 0;
});
},
or first occurrence:
function arrayFirstUnique(array) {
return array.filter(function (a, b, c) {
// keeps first occurrence
return c.indexOf(a) === b;
});
},
Well, it's only javascript ECMAScript 5+, which means only IE9+, but it's nice for a development in native HTML/JS (Windows Store App, Firefox OS, Sencha, Phonegap, Titanium, ...).
That's because 0 is a falsy value in JavaScript.
this[i] will be falsy if the value of the array is 0 or any other falsy value.
Now using sets you can remove duplicates and convert them back to the array.
var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
console.log([...new Set(names)])
Another solution is to use sort & filter
var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
var namesSorted = names.sort();
const result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);
console.log(result);
If you're using Prototype framework there is no need to do 'for' loops, you can use http://prototypejs.org/doc/latest/language/Array/prototype/uniq/ like this:
var a = Array.uniq();
Which will produce a duplicate array with no duplicates. I came across your question searching a method to count distinct array records so after uniq() I used size() and there was my simple result.
p.s. Sorry if i mistyped something
edit: if you want to escape undefined records you may want to add compact() before, like this:
var a = Array.compact().uniq();
I had a slightly different problem where I needed to remove objects with duplicate id properties from an array. this worked.
let objArr = [{
id: '123'
}, {
id: '123'
}, {
id: '456'
}];
objArr = objArr.reduce((acc, cur) => [
...acc.filter((obj) => obj.id !== cur.id), cur
], []);
console.log(objArr);
If you're okay with extra dependencies, or you already have one of the libraries in your codebase, you can remove duplicates from an array in place using LoDash (or Underscore).
Usage
If you don't have it in your codebase already, install it using npm:
npm install lodash
Then use it as follows:
import _ from 'lodash';
let idArray = _.uniq ([
1,
2,
3,
3,
3
]);
console.dir(idArray);
Out:
[ 1, 2, 3 ]
I'm not sure why Gabriel Silveira wrote the function that way but a simpler form that works for me just as well and without the minification is:
Array.prototype.unique = function() {
return this.filter(function(value, index, array) {
return array.indexOf(value, index + 1) < 0;
});
};
or in CoffeeScript:
Array.prototype.unique = ->
this.filter( (value, index, array) ->
array.indexOf(value, index + 1) < 0
)
Finding unique Array values in simple method
function arrUnique(a){
var t = [];
for(var x = 0; x < a.length; x++){
if(t.indexOf(a[x]) == -1)t.push(a[x]);
}
return t;
}
arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]
It appears we have lost Rafael's answer, which stood as the accepted answer for a few years. This was (at least in 2017) the best-performing solution if you don't have a mixed-type array:
Array.prototype.getUnique = function(){
var u = {}, a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
If you do have a mixed-type array, you can serialize the hash key:
Array.prototype.getUnique = function() {
var hash = {}, result = [], key;
for ( var i = 0, l = this.length; i < l; ++i ) {
key = JSON.stringify(this[i]);
if ( !hash.hasOwnProperty(key) ) {
hash[key] = true;
result.push(this[i]);
}
}
return result;
}
strange this hasn't been suggested before.. to remove duplicates by object key (id below) in an array you can do something like this:
const uniqArray = array.filter((obj, idx, arr) => (
arr.findIndex((o) => o.id === obj.id) === idx
))
For an object-based array with some unique id's, I have a simple solution through which you can sort in linear complexity
function getUniqueArr(arr){
const mapObj = {};
arr.forEach(a => {
mapObj[a.id] = a
})
return Object.values(mapObj);
}

How step through elements of an Array or properties of an Object for Recursion Functions

I apologize if this is a dumb question. I can't really find any resources via google that go through this topic. I don't understand how to step through an array of properties of an object in a recursion function since by definition a recursion will loop through itself. I know how to iterate through an array without a for loop in a recursion. What I don't understand is how to loop through an object for a Recursion. This is just some code I made up to demonstrate my lack of understanding.
var input1 = [1, 2, 3, 4, 5];
var input2 = {1: 'a', 2: 'b', 3: 'c'};
//for arrays
var arrayRecursion = function(someArray) {
var result = [];
//base case
if (someArray.length === 0) {
return result;
} else {
result.push(someArray.slice(0, 1));
return result.concat(arrayRecursion(someArray.slice(1)));
}
}
//for objects trying to copy input into results
var objectRecursion = function(someObject) {
var result = {};
for (var value in someObject) {
//base case
if (typeof(someObject[key]) !== 'object') {
return result;
}
//recursion
}
}
The main question I have is for my object recursion. If I have an established for - in loop for an object. How does it ever iterate through it? I don't have a recursion filled in because I have no clue how to approach this. If I call the recursion for the object, does it move onto the next property of the object? If so, how? Wouldn't you be starting the for - in loop all over again from the start? I guess where my logic lies is that the for loop is NOT continued from every recursion called because it executes the function which starts the loop from the first property
for..in loops iterate over properties, not values - (var value in someObject) will be quite misleading and result in bugs.
Once you have a reference to a value of the object, check whether it's an object or not. If it's an object, call the recursive objectRecursion and assign the result to the result object at the same property. (Don't return at this point, since that'll terminate the function)
Note that typeof is a keyword, not a function - don't put parentheses after it.
A related issue is that null's typeof is object too, so you'll have to compare against that as well.
var input2 = {1: 'a', 2: 'b', 3: 'c', foo: { prop: 2 }};
const objectRecursion = (someObject) => {
const result = {};
for (const [key, value] of Object.entries(someObject)) {
result[key] = typeof value === 'object' && value !== null
? objectRecursion(value)
: value;
}
return result;
};
console.log(objectRecursion(input2));
For a more flexible function which handles and copies arrays as well:
var input2 = {1: 'a', 2: 'b', 3: 'c', foo: { prop: 2, prop2: [3, 4, 5, { nested: 'nested' }] }};
const objectRecursion = (someItem) => {
if (typeof someItem !== 'object' && someItem !== null) {
return someItem;
}
if (Array.isArray(someItem)) {
return someItem.map(objectRecursion);
}
const result = {};
for (const [key, value] of Object.entries(someItem)) {
result[key] = objectRecursion(value)
}
return result;
};
console.log(objectRecursion(input2));
This should work recursively, using apply
https://jsfiddle.net/cz1frnL8/
var o = {1: 'a', 2: 'b', 3: 'c', foo: { prop: 2, prop2: [3, 4, 5, { nested: 'nested' }] }};
function process(key,value) {
console.log(key + " : "+value);
}
function traverse(o,func) {
for (var i in o) {
func.apply(this,[i,o[i]]);
if (o[i] !== null && typeof(o[i])=="object") {
traverse(o[i],func);
}
}
}
traverse(o,process);
My experience with using recursion with Objects has mainly been with recursing through nested objects rather than through sets of keys and values on the same object. I think this is because recursion as a pattern lends itself naturally to things that are fractal -- that is, where the data being operated on at each level of recursive depth is structurally similar.
Trees are a great example of this. Suppose I have a tree of node objects with the following structure:
4 - 8 - 9
| |
2 5 - 7
|
1
As a JS object, it might look like this.
{
val: 4,
left: {
val: 2,
left: {
val: 1
}
},
right: {
val: 8,
left: {
val: 5,
right: {
val: 7
}
},
right: {
val: 9
}
}
}
Notice how if I were to look at the object representing the left or right node from the root, it's structured the same as its parent? They're each effectively their own tree, but combined into a larger tree (this is what I mean by fractal).
If you wanted to find the largest value in this tree, you could do so by using recursion to iterate through the branches.
const getLargest = function (node) {
return Math.max(node.val, getLargest(node.left), getLargest(node.right));
};
That said, it's totally possible to use recursion on smaller and smaller sets of key-value pairs within an object. It might look something like this:
const exampleObject = {
a: 1,
b: 2,
c: 3
};
const recurse = function(obj) {
const keys = Object.keys(obj);
const firstKey = keys[0];
console.log(obj[firstKey]); // Or whatever; do a thing with the first key-value pair.
const smallerObj = Object.assign({}, obj); // Create a clone of the original; not necessary, but probably a good idea.
delete smallerObj[firstKey]; // Remove the key that we've just used.
recurse(smallerObj);
};
It's a little less natural in JS, but still totally doable. JavaScript object keys aren't sorted, but you could add a sort to const keys = Object.keys(obj) if you wanted to run through the keys in some specific order.

.flat() is not a function, what's wrong?

The following code
function steamrollArray(arr) {
// I'm a steamroller, baby
return arr.flat();
}
steamrollArray([1, [2], [3, [[4]]]]);
returns
arr.flat is not a function
I tried it in Firefox and Chrome v67 and the same result has happened.
What's wrong?
The flat method is not yet implemented in common browsers (only Chrome v69, Firefox Nightly and Opera 56). It’s an experimental feature. Therefore you cannot use it yet.
You may want to have your own flat function instead:
Object.defineProperty(Array.prototype, 'flat', {
value: function(depth = 1) {
return this.reduce(function (flat, toFlatten) {
return flat.concat((Array.isArray(toFlatten) && (depth>1)) ? toFlatten.flat(depth-1) : toFlatten);
}, []);
}
});
console.log(
[1, [2], [3, [[4]]]].flat(2)
);
The code was taken from here by Noah Freitas originally implemented to flatten the array with no depth specified.
This can also work.
let arr = [ [1,2,3], [2,3,4] ];
console.log([].concat(...arr))
Or for older browsers,
[].concat.apply([], arr);
Array.flat is not supported by your browser. Below are two ways to implement it.
As a function, the depth variable specifies how deep the input array structure should be flattened (defaults to 1; use Infinity to go as deep as it gets) while the stack is the flattened array, passed by reference on recursive calls and eventually returned.
function flat(input, depth = 1, stack = [])
{
for (let item of input)
{
if (item instanceof Array && depth > 0)
{
flat(item, depth - 1, stack);
}
else {
stack.push(item);
}
}
return stack;
}
As a Polyfill, extending Array.prototype if you prefer the arr.flat() syntax:
if (!Array.prototype.flat)
{
Object.defineProperty(Array.prototype, 'flat',
{
value: function(depth = 1, stack = [])
{
for (let item of this)
{
if (item instanceof Array && depth > 0)
{
item.flat(depth - 1, stack);
}
else {
stack.push(item);
}
}
return stack;
}
});
}
Similar issue, solved by using ES6 .reduce() method:
const flatArr = result.reduce((acc, curr) => acc.concat(curr),[]);
use _.flatten from lodash package ;)
var arr=[[1,2],[3,4],[5,6]];
var result=[].concat(...arr);
console.log(result); //output: [ 1, 2, 3, 4, 5, 6 ]
Another simple solution is _.flattenDeep() on lodash
https://lodash.com/docs/4.17.15#flattenDepth
const flatArrays = _.flattenDeep([1, [2], [3, [[4]]]]);
console.log(flatArrays);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
const array = [
[
[6, 6],
[3, 3],
],
[[7, 7, [9]]],
]
function simplifyArray(array) {
const result = []
function recursivePushElem(arr) {
arr.forEach(i => {
if (Array.isArray(i)) recursivePushElem(i)
else result.push(i)
})
}
recursivePushElem(array)
console.log(result)
return result
}
simplifyArray(array)
you could simply use this [].concat(...objArrs) that would work the same as the flat() method and allow more compatibility in browsers
You can set your full array to a string then split it. .toString().split(',')
Updated due to community bot.
So basically if you want to flatten out an array that does contain any objects but strictly strings or numbers, by using .toString() it converts each element of the array to a string (if it isn't already), and then joins all of the elements together using a comma as a separator.
Once we have our string all separated by a comma we can use .split() to create an array.
NOTE*** The reason this wont work with objects is that .toString() will return [object object] as it is the default string representation of an object in JavaScript.
If your array consists solely of numbers than you would need to map through your array and convert each string number value to a number.
const array1 = [
['one', 'oneTwo'],
'two',
'three',
'four',
]
console.log('a1', array1.toString().split(','))
const numberArray = [1, 2, [3, 4, [5, 6]], [[7, [8,9]]], 10];
console.log(numberArray.toString().split(',').map(num => Number(num)));
Not sure if it is a valid answer however in my attemp to flat an array I employed the destructuring_assignment introduced in ES6.
// typeScriptArray:Array<Object> = new Array<Object>();
let concatArray = [];
let firstArray = [1,2,3];
let secondArray = [2,3,4];
concatArray.push(...firstArray);
concatArray.push(...secondArray);
console.log(concatArray);
It works like a charm even though I'm not sure if any broswer compatibily issues may arise.

Using spread operator to extend array when using map

I'm not sure if this is a correct approach, but I'm curious if it can be done. I have an object from which I need to create an array, the key is the item, and the value id the number of times it repeats in the array.
const arrayInstructions = {
'm': 5,
's': 5,
'p': 5
}
Which should make ['m','m','m','m','m','s','s' ... ]
This is the working approach:
var array = []
Object.keys(arrayInstructions).forEach(function (agenda) {
array = array.concat( _.fill(Array(arrayInstructions[agenda]), agenda) )
})
Can it be done in this manner:
var deck = Object.keys(streamDeck).map(function (agenda) {
var partial = _.fill(Array(streamDeck[agenda]), agenda)
return ...partial // I know this is wrong
})
You could use spread on Array.prototype.concat to build the final array:
const arrayInstructions = { 'm': 5, 's': 5, 'p': 5 };
// This works because Array.prototype behaves like an empty list here
const deck = Array.prototype.concat(
..._.map(arrayInstructions, (times,agenda) => _.times(times, _=>agenda))
);
log(JSON.stringify(deck));
function log(x) { document.getElementsByTagName('pre')[0].appendChild(document.createTextNode(x)); }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.5.1/lodash.js"></script>
<pre></pre>
UPDATE
Considering Bergi's recommendation, this version is shorter and does not rely on the behaviour of Array.prototype in array context:
const deck = [].concat(..._.map(arrayInstructions,
(times,agenda) => _.times(times, _=>agenda)
));
There's no good reason to use a spread operator here?
In it's simplest form, the spread operator lets you use an array where multiple "elements" or arguments are expected, for your example something like
var part = [2, 3];
var arr = [1, ...part, 4]; // [1,2,3,4]
which does seem useful, but you're filling arrays and joining them together, and using concat seems more appropriate, however you can limit this to one single call to concat if you use apply and return a map
"use strict"
const arrayInstructions = {
'm': 5,
's': 1,
'p': 2
}
var deck = [].concat.apply([], Object.keys(arrayInstructions).map(function(k) {
return new Array(arrayInstructions[k]).fill(k)
}));
//output
document.body.innerHTML = '<pre>' + JSON.stringify(deck, 0, 4) + '</pre>';
As Bergi said JS does not have concatMap, but you can define this functionality manually:
function concatMap(array, func, thisArg) {
return [].concat.apply([], [].map.call(array, func, thisArg));
}
concatMap(
Object.keys(arrayInstructions),
k => new Array(arrayInstructions[k]).fill(k)
);

iterate over nested arrays and provide access to each index

I am going through a lesson on Javascript Arrays where we have to understand certain what is under the hood of underscoreJS methods. I need to write a function for the _.each method which will allow me to iterate over a single array and return a modified nested array that includes the index for each value.
For example:
var myArray = ['a', 'b', 'c'];
after the method each is called on myArray the new array should look like:
myArray = [ [ 'a', 0 ], [ 'b', 1 ], [ 'c', 2 ] ];
I have been searching on google for a day and have not found anything specific to this task. Stuck and need some help! Thank you.
_.each iterates over your array and has the following signature
function(value, index, array)
So lets see how it could be done ...
var result = [];
_.each(myArray, function(value, index, array){
result.push([value, index]); // <= does the work
}
This is not the ideal way (you should use map) but does illustrate how the each works.
Good luck
You can do it like below by using Array.prototype.reduce() function,
var myArray = ['a', 'b', 'c'];
var result = myArray.reduce(function(a,b,i){
return (a.push([b,i]), a)
},[]);
console.log(result); // [['a',0],['b',1],['c',2]];
Or as #bergi said you can do it with Array.prototype.map() also,
var myArray = ['a', 'b', 'c'];
var result = myArray.map(function(itm,i){
return [itm,i];
});
console.log(result); // [['a',0],['b',1],['c',2]];
Just use Array#map():
The map() method creates a new array with the results of calling a provided function on every element in this array.
var myArray = ['a', 'b', 'c'],
result = myArray.map(function (a, i) {
return [a, i];
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
If you're learning _.each method, you can do it like this
var myArray = ['a', 'b', 'c'];
_.each(myArray, function (e, i) {
myArray[i] = [e, i];
});
document.write(JSON.stringify(myArray));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
But for your case better to use _.map function

Categories