I receive a data value from a API, and I want to make a condition to deal with it. Sometimes it can comes as an array or object. I will use a simple example.
data = [1,2,3] // These values come from API
data.map(i => i++)
The problem is: Sometimes data can also comes as this
data = {
arr: [1,2,3]
}
// It evals an error in .map, because now "data" is an object
I know that I can solve it making something like this:
if(Array.isArray(data))
data.map(i => i++);
else
data.arr.map(i => i++);
But my code is not just a one line .map. Is there a way to make this simple condition without copying and paste code?
Thanks!
You can for example assign the array reference to another variable and use it in the rest of your code, like this:
let arr = Array.isArray(data) ? data : data.arr;
arr.map(i => i++)
A simple OR (||) operator is pretty idiomatic JavaScript:
(data.arr || data).map(i => ++i);
If the data.arr property is defined, that will be mapped, otherwise data itself will be mapped.
Complete snippet:
Note: the post-increment operator would have no effect, so I replaced it with a pre-increment.
let data, result;
data = [1, 2, 3];
result = (data.arr || data).map(i => ++i);
console.log(result);
data = {
arr: [1, 2, 3]
}
result = (data.arr || data).map(i => ++i);
console.log(result);
You can use the ternary operator.
Array.isArray(data)
? data.map(i => i++);
: data.arr.map(i => i++);
You can use destruction es6 , not sure its a good idea but you can achieve your functionality in single line. ;)
let { arr=data } = data;
arr.map(i => i++)
if arr key is not found in data then it will assign default data array.
Cheers
You can do it like this
You can use the ternary operator and assign the value as array directly to temp if it is an Array and if not than you assign using the property like input.arr which is an Array.
So once the value is in form of array than you can use the single map statement so you need not to repeat your map statement.
let data = [1,2,3];
let data1 = {
arr: [1,2,3]
}
function handle(input){
let temp = Array.isArray(input) ? input : input.arr
return temp.map(i => i++);
}
console.log(handle(data))
console.log(handle(data1))
If you don't wish to use an if or a ternary operator you can use Object.values(data).flat() to convert your data into:
[1, 2, 3]
This will essentially not modify your array and leave it be, however, it will compress your data object into an array form.
See working examples below:
Data form 1 (obj):
const data = {arr: [1, 2, 3]};
const res = Object.values(data).flat().map(i => ++i);
console.log(res);
Data form 2 (array):
const data = [1, 2, 3];
const res = Object.values(data).flat().map(i => ++i);
console.log(res);
Do note, however, Object.values does not guarantee order, and thus your array may lose its order. Moreover, if you plan to use this in production .flat() isn't yet supported across all browsers and instead, you may consider looking at a polyfill option
Related
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]))
Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
I don't know if this has already been proposed or asked before; Google searches returned only a myriad number of questions related to the current functionality of Array.push().
Here's an example implementation of this functionality, feel free to correct it:
;(function() {
var _push = Array.prototype.push;
Array.prototype.push = function() {
return this[_push.apply(this, arguments) - 1];
}
}());
You would then be able to do something like this:
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray.push({}));
Where someFunction modifies the object passed in as the second parameter, for example. Now the contents of someArray are [{"someKey": "hello world"}].
Are there any drawbacks to this approach?
See my detailed answer here
TLDR;
You can get the return value of the mutated array, when you instead add an element using array.concat[].
concat is a way of "adding" or "joining" two arrays together. The awesome thing about this method, is that it has a return value of the resultant array, so it can be chained.
newArray = oldArray.concat[newItem];
This also allows you to chain functions together
updatedArray = oldArray.filter((item) => {
item.id !== updatedItem.id).concat[updatedItem]};
Where item = {id: someID, value: someUpdatedValue}
The main thing to notice is, that you need to pass an array to concat.
So make sure that you put your value to be "pushed" inside a couple of square brackets, and you're good to go.
This will give you the functionality you expected from push()
You can use the + operator to "add" two arrays together, or by passing the arrays to join as parameters to concat().
let arrayAB = arrayA + arrayB;
let arrayCD = concat(arrayC, arrayD);
Note that by using the concat method, you can take advantage of "chaining" commands before and after concat.
Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
Of course there is one: Other code will expect Array::push to behave as defined in the specification, i.e. to return the new length. And other developers will find your code incomprehensible if you did redefine builtin functions to behave unexpectedly.
At least choose a different name for the method.
You would then be able to do something like this: someFunction(value, someArray.push({}));
Uh, what? Yeah, my second point already strikes :-)
However, even if you didn't use push this does not get across what you want to do. The composition that you should express is "add an object which consist of a key and a value to an array". With a more functional style, let someFunction return this object, and you can write
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
return obj;
}
someArray.push(someFunction(value, {}));
Just as a historical note -- There was an older version of JavaScript -- JavaScript version 1.2 -- that handled a number of array functions quite differently.
In particular to this question, Array.push did return the item, not the length of the array.
That said, 1.2 has been not been used for decades now -- but some very old references might still refer to this behavior.
http://web.archive.org/web/20010408055419/developer.netscape.com/docs/manuals/communicator/jsguide/js1_2.htm
By the coming of ES6, it is recommended to extend array class in the proper way , then , override push method :
class XArray extends Array {
push() {
super.push(...arguments);
return (arguments.length === 1) ? arguments[0] : arguments;
}
}
//---- Application
let list = [1, 3, 7,5];
list = new XArray(...list);
console.log(
'Push one item : ',list.push(4)
);
console.log(
'Push multi-items :', list.push(-9, 2)
);
console.log(
'Check length :' , list.length
)
Method push() returns the last element added, which makes it very inconvenient when creating short functions/reducers. Also, push() - is a rather archaic stuff in JS. On ahother hand we have spread operator [...] which is faster and does what you needs: it exactly returns an array.
// to concat arrays
const a = [1,2,3];
const b = [...a, 4, 5];
console.log(b) // [1, 2, 3, 4, 5];
// to concat and get a length
const arrA = [1,2,3,4,5];
const arrB = [6,7,8];
console.log([0, ...arrA, ...arrB, 9].length); // 10
// to reduce
const arr = ["red", "green", "blue"];
const liArr = arr.reduce( (acc,cur) => [...acc, `<li style='color:${cur}'>${cur}</li>`],[]);
console.log(liArr);
//[ "<li style='color:red'>red</li>",
//"<li style='color:green'>green</li>",
//"<li style='color:blue'>blue</li>" ]
var arr = [];
var element = Math.random();
assert(element === arr[arr.push(element)-1]);
How about doing someArray[someArray.length]={} instead of someArray.push({})? The value of an assignment is the value being assigned.
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray[someArray.length]={});
console.log(someArray)
I am trying to solve a problem which states to remove(delete) the smallest number in an array without the order of the elements to the left of the smallest element getting changed . My code is -:
function removeSmallest(numbers){
var x = Math.min.apply(null,numbers);
var y = numbers.indexOf(x);
numbers.splice(y,1);
return numbers;
}
It is strictly given in the instructions not to mutate the original array/list. But I am getting an error stating that you have mutated original array/list .
How do I remove the error?
Listen Do not use SPLICE here. There is great known mistake rookies and expert do when they use splice and slice interchangeably without keeping the effects in mind.
SPLICE will mutate original array while SLICE will shallow copy the original array and return the portion of array upon given conditions.
Here Slice will create a new array
const slicedArray = numbers.slice()
const result = slicedArray.splice(y,1);
and You get the result without mutating original array.
first create a copy of the array using slice, then splice that
function removeSmallest(numbers){
var x = Math.min.apply(null,numbers);
var y = numbers.indexOf(x);
return numbers.slice().splice(y,1);
}
You can create a shallow copy of the array to avoid mutation.
function removeSmallest(numbers){
const newNumbers = [...numbers];
var x = Math.min.apply(null,newNumbers);
var y = newNumbers.indexOf(x);
newNumbers.splice(y,1);
return newNumbers;
}
array.slice() and [... array] will make a shallow copy of your array object.
"shallow" the word says itself.
in my opinion, for copying your array object the solution is:
var array_copy = copy(array);
// copy function
function copy(object) {
var output, value, key;
output = Array.isArray(object) ? [] : {};
for (key in object) {
value = object[key];
output[key] = (typeof value === "object") ? copy(value) : value;
}
return output;
}
Update
Alternative solution is:-
var arr_copy = JSON.parse(JSON.stringify(arr));
I'm not sure what the exact context of the problem is, but the goal might be to learn to write pure transformations of data, rather than to learn how to copy arrays. If this is the case, using splice after making a throwaway copy of the array might not cut it.
An approach that mutates neither the original array nor a copy of it might look like this: determine the index of the minimum element of an array, then return the concatenation of the two sublists to the right and left of that point:
const minIndex = arr =>
arr.reduce(
(p, c, i) => (p === undefined ? i : c < arr[p] ? i : p),
undefined
);
const removeMin = arr => {
const i = minIndex(arr);
return minIndex === undefined
? arr
: [...arr.slice(0, i), ...arr.slice(i + 1)];
};
console.log(removeMin([1, 5, 6, 0, 11]));
Let's focus on how to avoid mutating. (I hope when you say "remove an error" you don't mean "suppress the error message" or something like that)
There are many different methods on Array.prototype and most don't mutate the array but return a new Array as a result. say .map, .slice, .filter, .reduce
Telling the truth just a few mutate (like .splice)
So depending on what your additional requirements are you may find, say .filter useful
let newArray = oldArray.filter(el => el !== minimalElementValue);
or .map
let newArray = oldArray.map(el => el === minimalElementValue? undefined: el);
For sure, they are not equal but both don't mutate the original variable
Long story short, i'm looking for a way to create and fill 2D arrays using ES6, in an effort to avoid for loops. The created array should contain all 0s. I've tried many different approaches so i cant post all of them.
var [r, c] = [5, 5];
var m = Array(r).fill(Array(c).fill(0));
This works but it creates a bunch of instances of the same array, and adding slice Array(r).fill(Array(c).fill(0).slice()); doesn't help either.
I also tried creating the empty arrays and then looping trough them but that's a whole different problem, you apparently can't forEach() or map() an empty array, and i couldn't even loop through a filled one efficiently.
Am i missing something here? Are a whole lot of for loops the best way to approach this? It looks really messy and overly long. Any help appreciated.
Doing this worked for me:
var [r, c] = [5, 5];
var m = Array(r).fill().map(()=>Array(c).fill(0));
Basically just filling it with a dummy value so you can map over it
You could use Array.from that takes a callback function and inside return arrays with 0's using fill method.
const arr = Array.from(Array(2), () => Array(5).fill(0))
console.log(arr)
Or you could just create array where each element is number of elements in sub-array and then use map and fill methods.
const arr = [5, 5].map(e => Array(e).fill(0))
console.log(arr)
For those needing the same thing but with undefined as each value this also works.
const x = 100;
const y = 100;
const grid = [...new Array(x)].map(() => [...new Array(y)]);
To fill the array simply map the inner value.
This will make an array filled with 0 for each value.
const x = 100;
const y = 100;
const grid = [...new Array(10)].map(() => [...new Array(10)].map(() => 0));
const nthArray = (n) => Array.from(Array(n), () => Array(5).fill(0))
const arr = nthArray(3);
console.log(arr);
How to print the main diagonale of array without using cycle for(...) but using array methods instead?
const arr = [
[1,6,8,-1],
[0,2,-6,5],
[0,-5,3,6],
[9,-1,1,0] ];
Here elements arr[0][0] = 1, arr[1][1]=2, arr[2][2]=3, arr[3][3]=0 are elements of the main diagonale. We can print them using cycle for:
for (let i=0;i<arr.length;i++)
{
console.log(arr[i][i]);
}
But is there a possibility to print them using methods .forEach .map or another one?
You can use Array.prototype.map but essentially they use the loops inside:
var a = [
[1,6,8,-1],
[0,2,-6,5],
[0,-5,3,6],
[9,-1,1,0] ];
var r = a.map((v, i) => v[i]);
console.log(r);
You can use array methods to get the same result, but they use for-loops inside anyways and in this case a single for-loop looks more readable to me.
However, if you really wanted to, you could do it like so:
const arr = [
[1,6,8,-1],
[0,2,-6,5],
[0,-5,3,6],
[9,-1,1,0] ];
const result = arr.map((sub_array, index) => sub_array.filter((number, index2) => index === index2));
console.log(result);