how to use a variable in a loDash function - javascript

Good day,
here is my data schema:
the length of data can change depending on a query
var data = {
IP1: 1,2,3,4,5,6
IP2: 4,5,6,7,8,9
IP3: 1,7,8,5,9,6,3}
keys=Object.keys(data)
sum=[]
for (i=0;i<keys.length;i++){
k='o.'+keys[i]
sum.push(_.sumBy(data, function(o) { return k }))
}
I am working on Skywise/palantir at some point I need to sum all columns, using loDash sumBy function
My question : is there a method to have o.key[i] as a variable in the lodash function?
Thanks for your help.
Regards

If I understand you correctly, you're trying to get the sums of the columns (1, 4, 1 for example).
You can use lodash to create a function with _.flow(), that takes the values of the object (the arrays), transposes them (rows to columns) using _.unzip(), and then sums each column by mapping it with _.sum():
const { flow, values, unzip, map, sum } = _
const fn = flow(
values, // get the values of the object - a list of arrays
unzip, // unzip to transpose the arrays to columns
arrs => map(arrs, sum) // sum each column
)
const data = {
IP1: [1, 2, 3, 4, 5, 6],
IP2: [4, 5, 6, 7, 8, 9],
IP3: [1, 7, 8, 5, 9, 6, 3]
}
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
And the slightly cleaner lodash/fp version:
const { flow, values, unzip, map, sum } = _
const fn = flow(
values, // get the values of the object - a list of arrays
unzip, // unzip to transpose the arrays to columns
map(sum) // sum each column
)
const data = {
IP1: [1, 2, 3, 4, 5, 6],
IP2: [4, 5, 6, 7, 8, 9],
IP3: [1, 7, 8, 5, 9, 6, 3]
}
const result = fn(data)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>

Related

How to extract multiple columns from a two dimension array?

Could I have some guidance on how to extract multiple columns from a two-dimensional array like the below using JavaScript?
Array = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12],
];
the simple methods I've seen so far using forEach or map will allow extraction of ONE column at a time, is there a way to nest it somehow to extract whichever column index one desire?
Let's say the desire output column is 1,2 and 4.
Output = [
[1, 2, 4],
[5, 6, 8],
[9,10,12],
];
EDIT:
Another problem I need to resolve is how to remove a row if it's Array[i][1]=0 or empty.
Let say we have extra array elements...
Array = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12],
[13,0,15,16],
[17, ,19,20],
[21,22,23,24],
];
The desired output is now...
Output = [
[1, 2, 4],
[5, 6, 8],
[9,10,12],
[21,22,24],
];
You can .map() each row in arr to a new array which you can obtain by using an inner .map() on an array of columns/indexes which you wish to obtain. The inner map will map each index to its associated value from the row, giving you the values at each column for each row.
See example below:
const arr = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12],
];
const cols = [1, 2, 4];
const res = arr.map(r => cols.map(i => r[i-1]));
console.log(res);
Further details:
As you mentioned the first map exposes each inner array inside of arr to the inner mapping function:
cols.map(i => r[i-1])
This mapping function will loop through the indexes defined inside of cols and transform them into a new value. For example, say you're r is the second array:
[5, 6, 7, 8]
Performing cols.map(...) will loop over each element (denoted by i in the callback function) in [1, 2, 4]. For each element, we "transform" it into a new element:
i = 1
r = [5, 6, 7, 8]
new value: r[1-1] = 5
Next iteration we look at the next value in cols:
i = 2
r = [5, 6, 7, 8]
new value: r[2-1] = 6
Lastly, we look at the final value in cols:
i = 4
r = [5, 6, 7, 8]
new value: r[4-1] = 8
So the mapping function produces a new array which transforms the values from cols [1, 2, 4], into the values at those indexes in the current row to be [5, 6, 8]. This occurs for each inner array / row, producing the final result.
EDIT
As per your edit, you can apply the same logic from above to get your arrays with only the columns you desire. Once you have done that you can use .filter() to keep only the rows which have a truthy value in the second column. Keeping all the arrays with truthy values in the second column will remove the arrays with non-truthy values in the second column (falsy values) from your resulting array. 0 and undefined are both flasy values, so they are not kept.
See example below:
const arr = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12],
[13,0,15,16],
[17, ,19,20],
[21,22,23,24],
]
const cols = [1, 2, 4];
const col = 2;
const res = arr.filter(r => r[col-1]).map(r => cols.map(i => r[i-1]));
console.log(res);
If you have multiple columns you want to ignore, you can use .every() to ensure each (ie: every) column contains a truthy value. If they all do, then .every() will return true, keeping the column, otherwise, it will return false, removing the column:
const arr = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12],
[13,0,15,16],
[ ,17,19,20],
[21,22,23,24],
]
const cols = [1, 2, 4];
const col = [1, 2];
const res = arr.filter(r => col.every(c => r[c-1])).map(r => cols.map(i => r[i-1]));
console.log(res);
You can approach it the below given way. The solution is using the index parameter available in filter method .
Array.filter(currElem,index,array)
Since array is 0-indexed ,so I created the array with index you want in the data as [0,1,3] . You need to pass the data array and index array to the function .
var array = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12],
];
var arrIndex = [0,1,3];
function extractData(arr,indexArr) {
return arr.map(obj => {
return obj.filter((ob,index) => {
if(indexArr.includes(index)){return ob}
})
})
}
console.log(extractData(array,arrIndex));

Assigning key's to array objects

I'm trying to solve this problem. Essentially, I have a array of keys, and an array of values within objects, and I want those values to have keys.
Below is my best attempt so far - usually use python so this is a bit confusing for me.
var numbers = [3, 4, 5,6]
var selection = [[1, 2, 3, 4], [6, 5, 4, 3], [2, 9, 4]]
var result = [];
for (arr in selection) {
numbers.forEach(function (k, i) {
result[k] = arr[i]
})
};
console.log(result);
The output I'm looking for is like this,
results = [{3:1,4:2,5:3,6:4}, {..},..]
Love some pointers to getting the right output.
Note. This is for google appscript! So can't use certain javascript functions (MAP I think doesn't work, unsure of reduce).
Cheers!
Use map on selection and Object.assign
var numbers = [3, 4, 5, 6];
var selection = [
[1, 2, 3, 4],
[6, 5, 4, 3],
[2, 9, 4]
];
var result = selection.map(arr =>
Object.assign({}, ...arr.map((x, i) => ({ [numbers[i]]: x })))
);
console.log(result);
Create a separate function which take keys and values as arguments and convert it into object using reduce(). Then apply map() on selections and make an object for each subarray using that function
var numbers = [3, 4, 5,6]
var selection = [[1, 2, 3, 4], [6, 5, 4, 3], [2, 9, 4]]
function makeObject(keys, values){
return keys.reduce((obj, key, i) => ({...obj, [key]: values[i]}),{});
}
const res = selection.map(x => makeObject(numbers, x));
console.log(res)
Create a new object from scratch for each number array:
const selection = [
[1, 2, 3, 4],
[6, 5, 4, 3],
[2, 9, 4],
];
function objMaker(numarr) {
const numbers = [3, 4, 5, 6];
numarr.forEach((num, i) => (this[numbers[i]] = num));
}
console.info(selection.map(numarr => new objMaker(numarr)));

Combine arrays of identical length into array of objects

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.

Convert an Array to unique values only while maintaining the correct sequence

I have the following code:
function uniteUnique(arr) {
//Create a single Array of value
arr = arguments[0].concat(arguments[1], arguments[2]);
//Reduce the Array to unique values only
arr = arr.reduce((pre, curr) => {
//Some function to reduce values
});
return arr;
}
uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
The goal is to produce a single Array containing only unique values while maintaining the order.
Currently it returns:
[1, 3, 2, 5, 2, 1, 4, 2, 1]
I'm wanting to reduce this to:
[1, 3, 2, 5, 4]
You can use Set for that:
function uniteUnique(...args) {
return [...new Set([].concat(...args))];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
It maintains insertion order, and by nature only contains unique values.
In ES5 you could do it by maintaining the used values as properties of a temporary object, while building the result array:
function uniteUnique(/* args */) {
return [].concat.apply([], arguments).reduce(function (acc, v) {
if (!acc[0][v]) acc[0][v] = acc[1].push(v); // assigns new length, i.e. > 0
return acc;
}, [ Object.create(null), [] ])[1];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
You can use the Set object since it already keeps your values unique in one object:
const mySet = new Set([1, 3, 2, 5, 2, 1, 4, 2, 1]);
// returns: Set { 1, 3, 4, 5 };
const arrayUniques = [...mySet];
console.log(arrayUniques);
// returns: [1, 3, 4, 5];

Performant way to convert adjacency list to links for undirected graph

I have an adjacency list like below:
const list = [
[1, 6, 8],
[0, 4, 6, 9],
[4, 6],
[4, 5, 8],
// ...
];
I need to create a set of links for an undirected graph without duplicates (example bellow).
Such links as [0,1] and [1,0] are considered duplicates.
const links = [
[ 0, 1 ], // duplicates
[ 0, 6 ],
[ 0, 8 ],
[ 1, 0 ], // duplicates
[ 1, 4 ],
// ...
]
Right now I do it this way:
const links = new Set;
const skip = [];
list.forEach( (v, i) => {
v.forEach( j => {
if (skip.indexOf(j) === -1) {
links.add([i, j]);
}
})
skip.push(i);
})
I am wondering if there is a better pattern to solve this kind of task on massive arrays.
You could sort your link tuple values, skip the check skip.indexOf(j) and let Set take care of the duplicates.
You could take a stringed array as value for the for the set, because an array with only sorted value is checking with strict mode in the set.
A primitive data type, like string works best.
var list = [[1, 6, 8], [0, 4, 6, 9], [4, 6], [4, 5, 8]],
links = new Set;
list.forEach((v, i) => v.forEach(j => links.add([Math.min(i, j), Math.max(i, j)].join())));
console.log([...links]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use one object to store value: index that has already been used and then check that object before adding to array.
const list = [[1, 6, 8],[0, 4, 6, 9],[4, 6],[4, 5, 8],];
var o = {},r = []
list.forEach(function(e, i) {
e.forEach(function(a) {
if (o[i] != a) {
r.push([i, a])
o[a] = i
}
})
})
console.log(JSON.stringify(r))
With ES6 arrow functions you can write the same like this.
const list = [[1, 6, 8], [0, 4, 6, 9], [4, 6], [4, 5, 8],];
var o = {}, r = []
list.forEach((e, i) => e.forEach(a => o[i] != a ? (r.push([i, a]), o[a] = i) : null))
console.log(JSON.stringify(r))

Categories

Resources