Output of the code:
const arr = [1, 2, 3, 4, 5];
console.log([...arr + []]);
gives
[ '1', ',', '2', ',', '3', ',', '4', ',', '5' ]
I know ...arr would return array items (like 1 2 3 4 5) and number + [] gives string, but I really got confused on why , is been added to the output array.
Is it because the ...arr in console.log()
turns out to be [..."1, 2, 3, 4, 5" + []] in which the output is the same?
Or is the some magical explanation that I am unaware of?
Here is an explanation on + operator applied to arrays. So what happens is this:
arr + [] gives you a string "1,2,3,4,5"
Then that string is spreaded/splitted (with the spread syntax) into an array of characters of that string.
Adding on Nurbol's answer when you do
const arr = [1, 2, 3, 4, 5];
console.log([...arr + []]);
It will turn out to a string with each element inside the array converting it to a string. Since the comma is also here so it will be an element of the string array.
When you do it like this
const arr = [1, 2, 3, 4, 5];
console.log((arr + []).split())
It creates the single string of that array and then you create an array of a string specifying the split point.
array.toString() adds , after each element
const a = [1, 2, 3, 4, 5]
console.log(a.toString())
// -> '1,2,3,4,5'
console.log([...a.toString()])
// -> [ '1', ',', '2', ',', '3', ',', '4', ',', '5' ]
[...aray + []] converts array to string then adds empty string and then uses [...resultString] to build result
Related
I have an array and I have a path to a specific element.
const str = "[0].subArray[2]"
const arr = [
{ subArray: [1, 2, 3, 4, 5] },
{ subArray: [32, 321, 11]}
];
Is it possible somehow to display an element using a string path?
You could take a dynamic approach for an length of the path.
function getValue(object, path) {
return path
.replace(/\[/g, '.')
.replace(/\]/g, '')
.split('.')
.filter(Boolean)
.reduce((o, k) => (o || {})[k], object);
}
console.log(getValue([{ subArray: [1, 2, 3, 4, 5] }, { subArray: [32, 321, 11] }], "[0].subArray[2]"));
You can achieve the result with match.
use regex here
/[a-zA-Z]+|\d+/g
const str = "[0].subArray[2]";
const arr = [{ subArray: [1, 2, 3, 4, 5] }, { subArray: [32, 321, 11] }];
function getValue(arr) {
const [first, prop, index] = str.match(/[a-zA-Z]+|\d+/g);
return arr[first][prop][index];
}
const result = getValue(arr);
console.log(result);
You could extract the key values out of the string using replace with a regex for numbers only, then reconstruct an array to output the sections of your sub-arrays you are targeting using a conditional to check the index to locate array and sub-array keys/values.
const arr = [
{ subArray: [1, 2, 3, 4, 5] },
{ subArray: [32, 321, 11]}
];
const str1 = "[0].subArray[2].subArray[1].subArray[4].subArray[1]"
const str2 = "[1].subArray[2].subArray[0]"
function removeNums(val){
return val.replace(/[^0-9]/g, '')
}
function getArrayValues(str){
// create an array by splitting the string at the dots
const arrs = str.split('.')
const results = []
for(let i in arrs){
// get the first index to locate the proper subarray
// remove all but numbers from the indexes
const firstIndex = removeNums(arrs[0])
// skip the first index and get remaining indexes
// to get the rest of the subarrays values
if(i > 0){
// remove all but numbers from the indexes
const index = removeNums(arrs[i])
// construct an array of the proper values
results.push(arr[firstIndex].subArray[index])
}
}
return results
}
console.log(getArrayValues(str1))
console.log(getArrayValues(str2))
I have a backend API which gives data in this format when it is not empty. I have to always push any new data coming from user into 0th index of first array.
[
[
{
name: 'Name 1',
type: 'Type 1'
},
{
name: 'Name 2',
type: 'Type 2'
}
],
[
{
name: 'Name 4',
type: 'Type 4'
},
{
name: 'Name 5',
type: 'Type 5'
}
]
]
The below code works fine with non empty data. But, if the API data is empty, it gives Cannot read property 'push' of undefined error.
arr: any = [];
constructor() {}
submit(data){
const dataObj = {
name: 'Test name',
type: 'Test type',
}
this.arr[0].push(dataObj)
console.log('Result array - ', this.arr)
}
I created a working example using Stackblitz. Could anyone please help?
You can test if it's empty, and first push an empty array if it is:
submit(data){
const dataObj = {
name: 'Test name',
type: 'Test type',
}
if (!this.arr[0]) {
this.arr.push([]);
}
this.arr[0].push(dataObj);
console.log('Result array - ', this.arr);
}
You are also saying you want to push it to the 0th index of first array. So this solves the 'first array' part. If you really want to push it to the 0th index of the first array, you use unshift:
this.arr[0].unshift(dataObj);
You can use unshift for it.
const arr = [1, 2, 3];
arr.unshift(0); // [0, 1, 2, 3];
Please note that it is a mutating function.
You can also use:
const arr = [1, 2, 3];
[0].concat(arr); // [0, 1, 2, 3];
This doesn't mutate the existing array.
There are suprisingly multiple ways to enter an element in the 0th position. You could achieve it with push() and shift() like other answers have mentioned. You could also do it with splice() method.
const arr1 = [1, 2, 3];
arr1.splice(0, 0, 4); // <-- enter in pos. 0
console.log('Position 0: ', arr1);
const arr2 = [1, 2, 3];
arr2.splice(1, 0, 4); // <-- enter in pos. 1
console.log('Position 1:', arr2);
I have 10 arrays of data that look like this:
var arr = [1,2,3,4,5,6,7,8,9,10]
var arr2=['hello','hello1','hello2','hello3','hello4','hello5','hello6','hello7','hello8','hello9']
...8 More Arrays
Each array will have exactly the same number of elements every time. I wanted to know the best way to generate an array of objects that look like this that combines the various arrays:
overallarray = [{
arr1 = 1,
arr2 = 'hello'
...
},
{
arr1 = 2,
arr2 = 'hello1'
...
}]
I recognize that I can use a large number of for loops but am looking for a more optimized solution that someone might have.
This is where Array.map() will be your friend. You can iterate through any of the arrays (since they have the same number of elements) and then access each element by index to get the corresponding value for each array in your dataset, like so:
var arr = [0,1,2,3,4,5,6,7,8,9]
var arr2=['hello','hello1','hello2','hello3','hello4','hello5','hello6','hello7','hello8','hello9'];
var arr3=['foo','foo1','foo2','foo3','foo4','foo5','foo6','foo7','foo8','foo9'];
let mapped = arr.map((elem, index) => {
return {
arr1: arr[index],
arr2: arr2[index],
arr3: arr3[index]
}
});
console.log(mapped);
Edit: If you wanted to access them generically, you can add all of your arrays to one dictionary and iterate over the key/value pairs, like so:
var arr = [0,1,2,3,4,5,6,7,8,9]
var arr2=['hello','hello1','hello2','hello3','hello4','hello5','hello6','hello7','hello8','hello9'];
var arr3=['foo','foo1','foo2','foo3','foo4','foo5','foo6','foo7','foo8','foo9'];
// combine all arrays into single dataset
let data = {arr, arr2, arr3};
let mapped = arr.map((elem, index) => {
// iterate over the key/value pairs of the dataset, use the key to generate the
// result object key, use the value to grab the item at the current index of the
// corresponding array
return Object.entries(data).reduce((res, [key, value]) => {
res[key] = value[index];
return res;
}, {});
});
console.log(mapped);
Assuming arr1,arr2 are not desired names of resulting object properties, if you need something
that scales nicely for arbitrary number of data arrays
assigns arbitrary key names (not necessarily corresponding to array variable names, or, worse, property name(s) that can't be valid variable name are needed)
works muuuch faster than accepted solution ;)
You may do the following:
const arr1 = [1,2,3,4,5,6,7,8,9,10],
arr2=['hello','hello1','hello2','hello3','hello4','hello5','hello6','hello7','hello8','hello9'],
keyNames = ['id', 'greeting'],
group = (...arrays) => (keys) =>
arrays.reduce((res, arr, idx) =>
(arr.forEach((e,i) => res[i][keys[idx]] = e), res),
Array.from({length:arrays[0].length}, () => ({}))
)
console.log(group(arr1,arr2)(keyNames))
.as-console-wrapper {min-height:100%;}
Just iterate all arrays with 1 loop counter:
var dataArrayOne = [1, 2, 3, 4 ];
var dataArrayTwo = ["hello", "hello1", "hello2", "hello3" ];
...
var resultArray = [];
for (var i = 0; i < 4; i++)
{
var combined = {
arr1: dataArrayOne[I],
arr2: dataArrayTwo[i]
...
};
resultArray.push(combined);
}
You can get from this:
[ [1, 2, 3]
, [4, 5, 6]
, [7, 8, 9]
]
to this:
[ [1, 4, 7]
, [2, 5, 8]
, [3, 6, 9]
]
with this function:
const combine =
(...arrs) =>
[ arrs.map(xs => xs[0])
, ... ( arrs.every(xs => xs.length === 1)
? []
: combine(...arrs.map(xs => xs.slice(1)))
)
];
combine
( [1, 2, 3]
, [4, 5, 6]
, [7, 8, 9]
);
Then from this:
[ [1, 4, 7]
, [2, 5, 8]
, [3, 6, 9]
]
to this:
[ {arr1: 1, arr2: 4, arr3: 7}
, {arr1: 2, arr2: 5, arr3: 8}
, {arr1: 3, arr2: 6, arr3: 9}
]
with this function:
const to_obj =
(...arrs) =>
arrs.map(arr =>
Object.fromEntries(
arr.map((x, i) => [`arr${i+1}`, x])));
to_obj
( [1, 4, 7]
, [2, 5, 8]
, [3, 6, 9]
)
Hopefully connecting the two functions together is straightforward.
A note about performance
With exactly 10 arrays of 10 elements each, it is unlikely that you can tell whether a particular solution performs better than another. You should probably go for the solution that feels right in terms of readability or maintenance.
By these criteria you should probably exclude mine; just wanted to share a different approach.
Application of 'sortBy' producing unexpected results.
I've gotta be doing something stoopid. This is such a basic operation.
const input = [4,3,2,1];
const sort = list => R.sortBy(R.ascend(R.identity))(list);
console.log(sort(input)); // [ 4, 3, 2, 1 ]
I would expect the output of the 'console.log' invocation to be [ 1, 2, 3, 4 ], but it is not: the output is [ 4, 3, 2, 1 ], same as the input. What am I doing wrong?
As pointed out by Aadit M Shah in the comments you're not using sortBy correctly.
Here's quick overview of how to sort in Ramda:
sort
Returns a copy of the list, sorted according to the comparator function, which should accept two values at a time and return a negative number if the first value is smaller, a positive number if it's larger, and zero if they are equal.
One case use subtract to sort in ascending order:
sort(subtract, [4, 1, 2, 3]);
//=> [1, 2, 3, 4]
Or to sort in descending, just flip it:
sort(flip(subtract), [4, 1, 2, 3]);
//=> [4, 3, 2, 1]
sort simply expects a function that can accept two parameters which can be compared with < or >.
So how would you sort an array of strings? Strings can be compared with < or > but using subtract wouldn't make sense. This is where ascend (or descend) can be useful:
Makes an ascending comparator function out of a function that returns a value that can be compared with < and >.
sort(ascend(identity), ["b", "a", "B", "A"]);
//=> ["A", "B", "a", "b"]
And if you want to make a case insensitive comparison:
sort(ascend(toLower), ["b", "a", "B", "A"]);
//=> ["a", "A", "b", "B"]
sortBy
As we saw, sort expects that you supply it with a function that accepts two parameters that can be compared together using < or >. Numbers and strings can be compared with these operators so if you can give them to Ramda directly then:
sortBy(identity, [4, 1, 2, 3]);
//=> [1, 2, 3, 4]
is the same as:
sort(subtract, [4, 1, 2, 3]);
//=> [1, 2, 3, 4]
However as far as I can tell, sortBy will always sort things in ascending order.
sortWith
You use sortWith when you can have multiple sort criteria:
Sort by age in ascending order
Sort by name in descending order
sortWith([ascend(prop('age')), descend(prop('name'))], [
{age: 40, name: 'John'},
{age: 40, name: 'Zack'},
{age: 10, name: 'Liam'},
{age: 20, name: 'Bill'}
]);
//=> [
//=> {age: 10, name: "Liam"},
//=> {age: 20, name: "Bill"},
//=> {age: 40, name: "Zack"},
//=> {age: 40, name: "John"}
//=> ]
I have an array of data like this:
var data = [
// Columns
['Target ID', 'Source ID', 'Label'],
// Key: 1
[1, 2, 'String 1/2'],
[1, 3, 'String 1/3'],
[1, 4, 'String 1/4'],
[1, 5, 'String 1/5'],
// Key: 2
[2, 1, 'String 2/1'],
[2, 3, 'String 2/3'],
[2, 4, 'String 2/4'],
[2, 5, 'String 2/5'],
// Key: 3
[3, 1, 'String 3/1'],
[3, 2, 'String 3/2'],
[3, 4, 'String 3/4'],
[3, 5, 'String 3/5']
]
As you can see, I have duplicate items in the array above, ie:
[1, 2, 'String 1/2']
And:
[2, 1, 'String 2/1']
I can't figure out the logic to remove those kind of duplicates.
All what I have achived so far is to remove duplicated items by first key value (since the array/data is created from a CSV), ie. [1, 1, 'String 1/2'].
Basically my question is: how to remove duplicated items from the array above?
EDIT: Better explanation of my problem.
The string at the index 2 in the array has nothing to do with anything - it's just a descriptor.
First item in the array is the key - in the examples I provided they are - 1, 2, 3 - thus, a second item is related to the key elsewhere in the array, thus, a relation between 1, 2 and 2, 1 is a duplicate - and I want those removed.
EDIT 2: Added the column item in the array above:
So, now I hope it's more clear -- first item in each array is the Target ID, second is the Source ID and the third one is not relavant at all.
My goal and need is to remove all items which already occur in the array, if the Target ID === Source ID in any of the previous arrays/items.
I have tried something dumb as:
var in_target = [];
var in_source = [];
var reformed_data = [];
for(var x in data) {
var _target = data[x][0];
var _source = data[x][1];
if(in_target.indexOf( _target ) === -1) {
if(in_source.indexOf( _source) === -1) {
reformed_data.push( data );
}
}
in_target.push( _target );
in_source.push( _source );
}
console.log( reformed_data );
But is doesn't work and it's terribly slow, since I actually have a large piece of data.
You could filter the data with a hash table with a sorted key.
var data = [['1', '2', 'String 1/2'], ['1', '3', 'String 1/3'], ['1', '4', 'String 1/4'], ['1', '5', 'String 1/5'], ['2', '1', 'String 2/1'], ['2', '3', 'String 2/3'], ['2', '4', 'String 2/4'], ['2', '5', 'String 2/5'], ['3', '1', 'String 3/1'], ['3', '2', 'String 3/2'], ['3', '4', 'String 3/4'], ['3', '5', 'String 3/5']];
data = data.filter(function (a) {
var key = a.slice(0, 2).sort().join('|');
return !this[key] && (this[key] = true);
}, Object.create(null));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I can't figure out the logic to remove those kind of duplicates.
Try this approach
var arr = [
[1, 2, 'String 1/2'],
[1, 3, 'String 1/3'],
[1, 4, 'String 1/4'],
[1, 5, 'String 1/5'],
[2, 1, 'String 2/1'],
[2, 3, 'String 2/3'],
[2, 4, 'String 2/4'],
[2, 5, 'String 2/5'],
[3, 1, 'String 3/1'],
[3, 2, 'String 3/2'],
[3, 4, 'String 3/4'],
[3, 5, 'String 3/5']
];
var map = {}; //initialize this map
var output = arr.filter( function(item){
var key1 = item[0], key2 = item[1];
map[key1] = map[key1] || {}; //create a map of map[1] for chain of 1
map[key2] = map[key2] || {}; //create a map of map[2] for chain of 2
map[key1][key2] = true; //now mark the map[1][2] to true
return !map[ key2 ][ key1 ] ; //check if map[2][1] is not true
});
console.log( output );