Non-destructively reversing an array in Javascript - javascript

Say I have a function:
function linesReverser(lines) {
var localLines = lines.slice();
localLines[1].reverse();
return _.flatten(localLines);
}
And using it like so:
var input = [["Hello"],["Hello", "World"]["Attention", "Please"]];
var output1 = linesReverser(input); //["Hello", "World", "Hello", "Attention", "Please"]
var output2 = linesReverser(input); //["Hello", "Hello", "World", "Attention", "Please"]
Notice how the object reference is being shared. I am new to JS, but I thought copying the values would alleviate this issue (line.slice()), but it doesn't seem to work. Is this because of the nested arrays?
How can I non-destructively/immutably perform a reverse?

You can duplicate the array using ES6's spread operator, then destructively reverse the duplicate:
const arr1 = [1,2,3];
const arr2 = [...arr1].reverse();
// arr1 => [1,2,3]
// arr2 => [3,2,1]

You're making a shallow copy of the lines array. To copy the nested arrays, you need to slice each one.
var localLines = lines.map(function(arr) {
return arr.slice();
});
The .map method will return a new Array of the return values, which are a slice of each nested Array.
FWIW, here's a shorter version that will work in modern browsers, though I'd probably stick with the first one.
var localLines = lines.map(Array.apply.bind(Array, null));

A non-destructive approach to reverse an array in Javascript
ES6
var array = [1,2,3];
array.reduce((ary, ele) => {ary.unshift(ele); return ary}, []);
// => [3,2,1];
// array => [1,2,3];
ES5
array.reduce(function(obj, ele){
obj.unshift(ele);
return obj;
},[]);

You can safely (deep) copy a nested array with JSON.parse and JSON.stringify, therefore the reference is not shared. Then destructively reverse the duplicate:
let array1 = [[1, 3], [50, 1]];
let arrayCopy = JSON.parse(JSON.stringify(array1));
arrayCopy.reverse(); //[[50,1],[1,3]]
checkout cloning of arrays for more info

Related

Check if an array of arrays contains a value in javascript

I know that if there is an array of values it must be used this approach:
console.log(['joe', 'jane', 'mary'].includes('jane')); // true
But in case of an array of arrays, is there a short way to do it? Without other computations between.
For this input:
[['jane'],['joe'],['mary']]
You can use flat method to flatten the array. For more neted array, you can also mention depth like flat(depth)
let arr = [["jane"],["joe"],["mary"]];
arr.flat().includes('jane'); //true
You can easily achieve this result using some
arr.some((a) => a.includes("jane"))
const arr = [
["jane"],
["joe"],
["mary"]
];
const arr2 = [
["joe"],
["mary"]
];
console.log(arr.some((a) => a.includes("jane")));
console.log(arr2.some((a) => a.includes("jane")));
it can also be done by first flattening the 2d arrays in 1 d aaray and then using includes to find whether the array contains the element or not
var arr = [['jane'],['joe'],['marry']]
var newarr=[].concat(...arr)
var v=newarr.includes('jane')
console.log(v)

How to destructure an array of objects? [duplicate]

In its most basic form, having an array of objects:
let arr = [
{val:"a"},
{val:"b"}
];
How can destructuring be used, to obtain only the values ['a', 'b'].
getting the first value is easy:
let [{val:res}] = arr; //res contains 'a'
Obtaining all values inside the array can be done with the rest operator:
let [...res] = arr; //res contains all objects
Combining those, I expected to be able to use:
let [...{val:res}] = arr; //undefined, expected all 'val's (['a', 'b'])
The above returns undefined (Tested in FF). Some further testing seems to indicate that adding the rest operator when using an object destructuring as well doesn't use the iteration, but gets back the original object, e.g. let [...{length:res}] = arr; //res= 2. Some other trials, such as let [{val:...res}] = arr; or let [{val}:...res] = arr; produce syntax errors.
It's easy enough to do with other methods, such as using map on the array, but mostly I stumble upon this problem while destructuring multiple levels (an array with objects which have their own property containing an array). Therefore I'm really trying to get around how to do it solely with destructuring.
For convenience: a test fiddle
edit
My apologies if I failed to explain the goal of the question. I'm not looking for a solution to a specific problem, only to find the correct syntax to use when destructuring.
Otherwise formulated, a first question would be: in the example above, why doesn't let [...{val:res}] = arr; return all values (['a', 'b']). The second question would be: what is the proper syntax to use a rest operator with a nested object destructuring? (pretty sure I've gotten some definitions mixed up here). It seems that the latter is not supported, but I haven't come across any documentation that (and why) it wouldn't be.
Why doesn't let [...{val:res}] = arr; return all values (['a', 'b'])?
You seem to confuse the rest syntax with array comprehensions.
If you assign a value to [someElements, ...someExpression], the value is tested to be iterable and then each element generated by the iterator is assigned to the respective someElements variable. If you use the rest syntax in the destructuring expression, an array is created and the iterator is ran till its end while filling the array with the generated values. Then that array is assigned to the someExpression.
All of these assignment targets can be other destructuring expressions (arbitrarily nested and recursively evaluated), or references to variable or properties.
So if you do let [...{val:res}] = arr, it will create an array and fill that with all the values from the iterator of arr:
let {val:res} = Array.from(arr[Symbol.iterator]())
You can see now why that ends up with undefined, and why using something like [...{length:res}] does yield a result. Another example:
let [{val:res1}, ...{length: res2}] = arr;
console.log(res1) // 'a'
console.log(res2) // 1 (length of `[{val: 'b'}]`)
How can destructuring be used to obtain only the values ['a', 'b']?
Not at all. Use the map method.
You can destructure nested objects like this
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Nested_object_and_array_destructuring
let arr = [
{val:"a"},
{val:"b"}
];
const [{val: valueOfA}, {val: valueOfB}] = arr
console.log(
valueOfA, valueOfB
)
Beside mapping with a callback for the value
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(o => o.val));
you could use deconstructiong inside of the paramter list and use only the value to return.
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(({val}) => val));
At this point of time you can use both For of loop with ES6 Object destructuring.
let arr = [{val:"a"},{val:"b"}];
for (const item in arr){
const {val} = arr[item];
console.log(val);
}
You can declare assignment target before destructuring assignment; at destructuring target, set values of assignments target indexes by from destructuring source
let arr1 = [{val: "a"}, {val: "b"}];
let arr2 = [{"foo":1,"arr":[{"val":"a"},{"val":"b"}]}
, {"foo":2,"arr":[{"val":"c"},{"val":"d"}]}];
let [res1, res2] = [[], []];
[{val: res1[0]}, {val: res1[1]}] = arr1;
[{arr: [{val:res2[0]}, {val:res2[1]}]}
, {arr: [{val:res2[2]}, {val:res2[3]}]}] = arr2;
console.log(res1, res2);
You can alternatively use rest element at target to collect values at source by including comma operator following object pattern to return value pulled from object
let arr = [{val: "a"}, {val: "b"}];
let [...res] = [({val} = arr[0], val), ({val} = arr[1], val)];
console.log(res)

Reverse() doesn't work in array with string key

Why reverse() doesn't work if my array have elements with string as a key.
var myArray = [];
myArray["test"] = 100;
myArray["test2"] = 200;
console.log(myArray)
console.log(myArray.reverse())
Both returns the same result.
How can I change it to make it work?
DEMO: https://www.w3schools.com/code/tryit.asp?filename=GG4PXCHZ4VUD
.reverse() is a function of arrays, where elements are indexed by their position. Your code is not adding elements to the array, but rather adding properties on the array object. This works and the properties can be accessed, but reversing does nothing as these are not elements. The array is still of 0 length.
You will have to either:
Make myArray an object of a different type. In this case, reverse will still not work, and you will have to write code to sort manually. Other answers have provided some guidance as to how to achieve this
Add your elements to the array using push() or numeric indices, in which case you'll lose the string indices but can use array sorting methods such as .reverse()
While numbers have an intrinsic ordering, object property keys follow different rules. If you want to reverse a string-indexed object, consider writing a function to insert objects to a new Map in reverse order.
Arrays values are only accessible by index.
myArray[0] = 100;
myArray[1] = 200;
console.log(myArray) // [100, 200]
console.log(myArray.reverse()) // [200, 100]
You can store values by keys in objects.
var myObject = {};
myObject["test"] = 100;
myObject["test2"] = 200;
console.log(myObject) // {test: 100, test2: 200}
That said, you can reverse an object by doing this:
Object.entries(myObject).reverse().reduce((a, b) => {
a[b[0]] = b[1];
return a
},{})
Since you do not actually need any array methods, it would be better to simply use an object instead.
You can create a new reversed array by looping over Object.keys() backwards. See the code in action here.
var myArray = [];
myArray["test"] = 100;
myArray["test2"] = 200;
function reverseAssociative(arr){
const keys = Object.keys(arr);
const res = [];
for(let i = keys.length - 1; i >= 0; i--){
res[keys[i]] = arr[keys[i]];
}
return res;
}
const res = reverseAssociative(myArray);
for(const key in res){
console.log(key, res[key]);
}
You can simplify it by using reduce on Object.entries after reversing. See the code in action here.
myArray = Object.entries(myArray).reverse().reduce((acc,[key,val])=>(acc[key]=val,acc),[])

Destructure object properties inside array for all elements

In its most basic form, having an array of objects:
let arr = [
{val:"a"},
{val:"b"}
];
How can destructuring be used, to obtain only the values ['a', 'b'].
getting the first value is easy:
let [{val:res}] = arr; //res contains 'a'
Obtaining all values inside the array can be done with the rest operator:
let [...res] = arr; //res contains all objects
Combining those, I expected to be able to use:
let [...{val:res}] = arr; //undefined, expected all 'val's (['a', 'b'])
The above returns undefined (Tested in FF). Some further testing seems to indicate that adding the rest operator when using an object destructuring as well doesn't use the iteration, but gets back the original object, e.g. let [...{length:res}] = arr; //res= 2. Some other trials, such as let [{val:...res}] = arr; or let [{val}:...res] = arr; produce syntax errors.
It's easy enough to do with other methods, such as using map on the array, but mostly I stumble upon this problem while destructuring multiple levels (an array with objects which have their own property containing an array). Therefore I'm really trying to get around how to do it solely with destructuring.
For convenience: a test fiddle
edit
My apologies if I failed to explain the goal of the question. I'm not looking for a solution to a specific problem, only to find the correct syntax to use when destructuring.
Otherwise formulated, a first question would be: in the example above, why doesn't let [...{val:res}] = arr; return all values (['a', 'b']). The second question would be: what is the proper syntax to use a rest operator with a nested object destructuring? (pretty sure I've gotten some definitions mixed up here). It seems that the latter is not supported, but I haven't come across any documentation that (and why) it wouldn't be.
Why doesn't let [...{val:res}] = arr; return all values (['a', 'b'])?
You seem to confuse the rest syntax with array comprehensions.
If you assign a value to [someElements, ...someExpression], the value is tested to be iterable and then each element generated by the iterator is assigned to the respective someElements variable. If you use the rest syntax in the destructuring expression, an array is created and the iterator is ran till its end while filling the array with the generated values. Then that array is assigned to the someExpression.
All of these assignment targets can be other destructuring expressions (arbitrarily nested and recursively evaluated), or references to variable or properties.
So if you do let [...{val:res}] = arr, it will create an array and fill that with all the values from the iterator of arr:
let {val:res} = Array.from(arr[Symbol.iterator]())
You can see now why that ends up with undefined, and why using something like [...{length:res}] does yield a result. Another example:
let [{val:res1}, ...{length: res2}] = arr;
console.log(res1) // 'a'
console.log(res2) // 1 (length of `[{val: 'b'}]`)
How can destructuring be used to obtain only the values ['a', 'b']?
Not at all. Use the map method.
You can destructure nested objects like this
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Nested_object_and_array_destructuring
let arr = [
{val:"a"},
{val:"b"}
];
const [{val: valueOfA}, {val: valueOfB}] = arr
console.log(
valueOfA, valueOfB
)
Beside mapping with a callback for the value
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(o => o.val));
you could use deconstructiong inside of the paramter list and use only the value to return.
let arr = [{ val: "a" }, { val: "b" }];
console.log(arr.map(({val}) => val));
At this point of time you can use both For of loop with ES6 Object destructuring.
let arr = [{val:"a"},{val:"b"}];
for (const item in arr){
const {val} = arr[item];
console.log(val);
}
You can declare assignment target before destructuring assignment; at destructuring target, set values of assignments target indexes by from destructuring source
let arr1 = [{val: "a"}, {val: "b"}];
let arr2 = [{"foo":1,"arr":[{"val":"a"},{"val":"b"}]}
, {"foo":2,"arr":[{"val":"c"},{"val":"d"}]}];
let [res1, res2] = [[], []];
[{val: res1[0]}, {val: res1[1]}] = arr1;
[{arr: [{val:res2[0]}, {val:res2[1]}]}
, {arr: [{val:res2[2]}, {val:res2[3]}]}] = arr2;
console.log(res1, res2);
You can alternatively use rest element at target to collect values at source by including comma operator following object pattern to return value pulled from object
let arr = [{val: "a"}, {val: "b"}];
let [...res] = [({val} = arr[0], val), ({val} = arr[1], val)];
console.log(res)

How to convert Set to Array?

Set seems like a nice way to create Arrays with guaranteed unique elements, but it does not expose any good way to get properties, except for generator [Set].values, which is called in an awkward way of mySet.values.next().
This would have been ok, if you could call map and similar functions on Sets. But you cannot do that, as well.
I've tried Array.from, but seems to be converting only array-like (NodeList and TypedArrays ?) objects to Array. Another try: Object.keys does not work for Sets, and Set.prototype does not have similar static method.
So, the question: Is there any convenient inbuilt method for creating an Array with values of a given Set ? (Order of element does not really matter).
if no such option exists, then maybe there is a nice idiomatic one-liner for doing that ? like, using for...of, or similar ?
if no such option exists, then maybe there is a nice idiomatic
one-liner for doing that ? like, using for...of, or similar ?
Indeed, there are several ways to convert a Set to an Array:
Using Array.from:
Note: safer for TypeScript.
const array = Array.from(mySet);
Simply spreading the Set out in an array:
Note: Spreading a Set has issues when compiled with TypeScript (See issue #8856). It's safer to use Array.from above instead.
const array = [...mySet];
The old-fashioned way, iterating and pushing to a new array (Sets do have forEach):
const array = [];
mySet.forEach(v => array.push(v));
Previously, using the non-standard, and now deprecated array comprehension syntax:
const array = [v for (v of mySet)];
via https://speakerdeck.com/anguscroll/es6-uncensored by Angus Croll
It turns out, we can use spread operator:
var myArr = [...mySet];
Or, alternatively, use Array.from:
var myArr = Array.from(mySet);
Assuming you are just using Set temporarily to get unique values in an array and then converting back to an Array, try using this:
_.uniq([])
This relies on using underscore or lo-dash.
Perhaps to late to the party, but you could just do the following:
const set = new Set(['a', 'b']);
const values = set.values();
const array = Array.from(values);
This should work without problems in browsers that have support for ES6 or if you have a shim that correctly polyfills the above functionality.
Edit: Today you can just use what #c69 suggests:
const set = new Set(['a', 'b']);
const array = [...set]; // or Array.from(set)
Use spread Operator to get your desired result
var arrayFromSet = [...set];
The code below creates a set from an array and then, using the ... operator.
var arr=[1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,];
var set=new Set(arr);
let setarr=[...set];
console.log(setarr);
SIMPLEST ANSWER
just spread the set inside []
let mySet = new Set()
mySet.add(1)
mySet.add(5)
mySet.add(5)
let arr = [...mySet ]
Result: [1,5]
In my case the solution was:
var testSet = new Set();
var testArray = [];
testSet.add("1");
testSet.add("2");
testSet.add("2"); // duplicate item
testSet.add("3");
var someFunction = function (value1, value2, setItself) {
testArray.push(value1);
};
testSet.forEach(someFunction);
console.log("testArray: " + testArray);
value1 equals value2 => The value contained in the the current position in the Set. The same value is passed for both arguments
Worked under IE11.
Using Set and converting it to an array is very similar to copying an Array...
So you can use the same methods for copying an array which is very easy in ES6
For example, you can use ...
Imagine you have this Set below:
const a = new Set(["Alireza", "Dezfoolian", "is", "a", "developer"]);
You can simply convert it using:
const b = [...a];
and the result is:
["Alireza", "Dezfoolian", "is", "a", "developer"]
An array and now you can use all methods that you can use for an array...
Other common ways of doing it:
const b = Array.from(a);
or using loops like:
const b = [];
a.forEach(v => b.push(v));
the simplistic way to doing this
const array = [...new Set([1,1,2,3,3,4,5])]
console.log(array)
Here is an easy way to get only unique raw values from array. If you convert the array to Set and after this, do the conversion from Set to array. This conversion works only for raw values, for objects in the array it is not valid. Try it by yourself.
let myObj1 = {
name: "Dany",
age: 35,
address: "str. My street N5"
}
let myObj2 = {
name: "Dany",
age: 35,
address: "str. My street N5"
}
var myArray = [55, 44, 65, myObj1, 44, myObj2, 15, 25, 65, 30];
console.log(myArray);
var mySet = new Set(myArray);
console.log(mySet);
console.log(mySet.size === myArray.length);// !! The size differs because Set has only unique items
let uniqueArray = [...mySet];
console.log(uniqueArray);
// Here you will see your new array have only unique elements with raw
// values. The objects are not filtered as unique values by Set.
// Try it by yourself.
I would prefer to start with removing duplications from an array and then try to sort.
Return the 1st element from new array.
function processData(myArray) {
var s = new Set(myArray);
var arr = [...s];
return arr.sort((a,b) => b-a)[1];
}
console.log(processData([2,3,6,6,5]);
function countUniqueValues(arr) {
return Array.from(new Set(arr)).length
}
console.log(countUniqueValues([1, 2, 3, 4, 4, 4, 7, 7, 12, 12, 13]))

Categories