make function take multiple variables from an array passed in as parameter - javascript

How to make function take multiple variables from an array passed in as parameter?
Edited
For example:
Achieve this:
const inputObj = [
['Anna', 10, 'Monday'],
['Anna', 15, 'Wednesday'],
['Beatrice', 8, 'Monday'],
['Beatrice', 11, 'Wednesday'],
['Anna', 4, 'Wednesday'],
['Beatrice', 5, 'Monday'],
['Beatrice', 16, 'Monday']
]
// expected output:
const outputObj = [
[ 'Anna', 10, 'Monday' ],
[ 'Anna', 19, 'Wednesday' ],
[ 'Beatrice', 29, 'Monday' ],
[ 'Beatrice', 11, 'Wednesday' ]
]
const arr = [0, 2]
const someFunction = (obj, v, a) => {
const result = obj.reduce((acc, cur) => {
const key = `${cur[a[0]]}|${cur[a[1]]}`
if(!acc[key]) acc[key] = cur
else acc[key][1] += cur[v]
return acc
}, {})
return Object.values(result)
}
console.log(someFunction(inputObj, 1, arr))
with this:
const arr = [0, 2, 3, ...] // basically the array could contain any number of items.
const someFunction = (obj, v, objParams) => {
const result = obj.reduce((acc, cur) => {
const key = ???
...
}, {})
}
So that the function can be reused and it accepts custom-sized arrays, check if the column numbers in the array are the same, then adds the sum of the column that is passed in as v?
How to declare the variables from the objParams to achieve the same result as the code above does?
Also how to add v in the middle of cur?

Assuming objParams is an array of unknown size (strings in this example):
const objParams = ["c1", "c2", "c3"];
const key = objParams.join(']}|${cur[');
const built = '${cur[' + key + ']';
Built is:
${cur[c1]}|${cur[c2]}|${cur[c3]

With ES6 you can use the spread operator in the argument definition.
More reading about spread operator on MDN
function sum(...args) {
return args.reduce((result, value) => result + value, 0)
}
const numbers = [1, 2, 3];
console.log('sum', sum(2, 2));
console.log('sum', sum(...numbers));
console.log('sum', sum(1, 2, 1, ...numbers));
// get single args before accumulating the rest
function sum2(foo, bar, ...args) {
return args.reduce((result, value) => result + value, 0)
}
console.log('sum2', sum2(2, 2));
console.log('sum2', sum2(...numbers));
console.log('sum2', sum2(1, 2, 1, ...numbers));

Related

Transform nested object of objects of arrays in JS

Besides the horrible name of the question my question is quite simple. I have this object:
let test = {
date1: [
{
time: 1,
value: 5,
},
{
time: 2,
value: 6,
},
],
date2: [
{
time: 1,
value: 20,
},
{
time: 2,
value: 10,
},
],
};
That I want to transform to something like this:
let result = {
date1: {
values: [5, 6],
times: [1, 2],
},
date2: {
values: [1, 2], // easier to summarise?!
times: [10, 20],
},
};
I actually want to do this in order to summarise the value-values for each date. I thought that if I have them in an array it would be easier to summarise them. I know there are other forms to do this (and I'd be happy to see any solutions).
My current approach does not what I want it to do. It looks like this:
let keys = Object.keys(test);
let red = keys.reduce((acc, curr) => {
return (acc[curr] = test[curr].map((e) => e.value));
}, {});
console.log(`red: `, red);
And produces this:
red: [ 20, 10 ]
This
return (acc[curr] = test[curr].map((e) => e.value));
is equivalent to
acc[curr] = test[curr].map((e) => e.value);
return acc[curr];
going inside a nested key of the accumulator on every iteration - which isn't the logic you want. Return the whole accumulator on a separate line, so previously assigned values don't get lost, and you also need to account for both the time and value properties of the array being iterated over - your => e.value only extracts one of the two properties you want.
let test = {
date1: [
{
time: 1,
value: 5,
},
{
time: 2,
value: 6,
},
],
date2: [
{
time: 1,
value: 20,
},
{
time: 2,
value: 10,
},
],
};
const keys = Object.keys(test);
const result = keys.reduce((acc, key) => {
acc[key] = {
values: test[key].map(({ value }) => value),
times: test[key].map(({ time }) => time),
};
return acc;
return acc;
}, {});
console.log(result);
or do
let test = {
date1: [
{
time: 1,
value: 5,
},
{
time: 2,
value: 6,
},
],
date2: [
{
time: 1,
value: 20,
},
{
time: 2,
value: 10,
},
],
};
const result = Object.fromEntries(
Object.entries(test).map(([key, arr]) => [
key,
{
values: arr.map(({ value }) => value),
times: arr.map(({ time }) => time),
}
])
);
console.log(result);
Try modifying it a little like this:
let result = Object.keys(test).reduce((acc, key) => {
test[key].forEach((item) => {
acc.push({
date: key,
time: item.time,
value: item.value,
});
});
return acc;
}
, []);
console.log(result);
Assuming all inner objects have the same keys and no date array is empty:
let test = {date1:[{time:1,value:5},{time:2,value:6},],date2:[{time:1,value:20},{time:2,value:10},]};
let keys = Object.keys(test);
let red = keys.reduce((acc, curr) => ({
...acc,
[curr]: Object.keys(test[curr][0])
.reduce((acc, key) => ({
...acc,
[key + 's']: test[curr].map(o => o[key])
}), {})
}), {});
console.log(`red: `, red);
There is no need to first create arrays when you want to sum up values from different objects. It looks like you want to achieve this result:
{
date1: 11
date2: 30
}
The idea to use reduce is fine (for summing up values). You can use Object.entries and Object.fromEntries on top of that, in order to create the new object structure:
const test = {date1: [{time: 1,value: 5,},{time: 2,value: 6,},],date2: [{time: 1,value: 20,},{time: 2,value: 10,},],};
const result = Object.fromEntries(
Object.entries(test).map(([key, arr]) =>
[key, arr.reduce((sum, {value}) => sum + value, 0)]
)
);
console.log(result);

How to get element of array with string index path

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))

How to multiply certain columns/fields in a large array of objects?

I have an array as the following:
const data = [
{ Date: "2012", A: 2, B: 3, C: 4 },
{ Date: "2013", A: 4, B: 7, C: 8 },
{ Date: "2014", A: 0.1, B: 0.3, C: 0.4 },
];
I want to multiply specific columns/fields by 100, so I created a new array that holds their keys:
const arr = ["A", "B", "C"];
And then I use .map to multiple those columns:
arr.forEach((element, i) => data.map(a => a[element] * 100));
However, when I check the data array of objects it seems to be unchanged:
const data = [
{ Date: "2012", A: 2, B: 3, C: 4 },
{ Date: "2013", A: 4, B: 7, C: 8 },
{ Date: "2014", A: 0.1, B: 0.3, C: 0.4 },
];
const arr = ["A", "B", "C"];
arr.forEach((element, i) => data.map(a => a[element] * 100));
console.log(data);
You are not assigning the new values.
If you want to mutate the original elements, then instead of a[element] * 100 you should have a[element] = a[element] * 100 or a[element] *= 100.
If you don't want to mutate the original elements, then you should be assigning back the value map returns and creating new objects:
arr.forEach((element, i) => data = data.map(a => {
return { ...a, [element]: a[element] * 100 };
}));
If you are concerned about performance, you can use performance.now() to check how much time it takes to run different variations of your code:
const columns = ["A", "B", "C"];
let data1 = [];
const data2 = [];
let data3 = [];
const data4 = [];
for (let i = 0; i < 100000; ++i) {
data1.push({ Date: "2012", A: 2, B: 3, C: 4 });
data2.push({ Date: "2012", A: 2, B: 3, C: 4 });
data3.push({ Date: "2012", A: 2, B: 3, C: 4 });
data4.push({ Date: "2012", A: 2, B: 3, C: 4 });
}
// Original code with map (doesn't mutate original rows):
const t1 = performance.now();
columns.forEach(column => data1 = data1.map(row => {
return { ...row, [column]: row[column] * 100 };
}));
console.log(Object.values(data1[0]).join(', '), performance.now() - t1);
// Original code with assignment (no map, mutates original rows):
const t2 = performance.now();
columns.forEach(column => data2.forEach(row => row[column] *= 100));
console.log(Object.values(data2[0]).join(', '), performance.now() - t2);
// Iteraring rows first with map (doesn't mutate original rows):
const t3 = performance.now();
data3 = data3.map((row, i) => {
const newRow = { ...row };
columns.forEach(column => newRow[column] *= 100);
return newRow;
});
console.log(Object.values(data3[0]).join(', '), performance.now() - t3);
// Iteraring rows first with assignment (no map, mutates original rows):
const t4 = performance.now();
data4.forEach((row, i) => columns.forEach(column => row[column] *= 100));
console.log(Object.values(data4[0]).join(', '), performance.now() - t4);
Also, you might want to consider using a for or while loop, which might be faster than .map or .forEach.
You could combine map and reduce to create new array of objects and keep the original data.
var data = [{Date:"2012", A:2, B:3, C:4}, {Date:"2013", A:4, B:7, C:8}, {Date:"2014", A:0.1, B:0.3, C:0.4}]
var arr = ["A","B","C"]
// map over data array and return a new object for each element
const result = data.map(o => ({
// spread current object from original array
...o,
// spread object returned from reduce that will overide original properties
...arr.reduce((r, k) => {
// check if the current key ('A', 'B'...) exists in current object
// and if it does create new property where the value is
// original value * 100
if (o[k]) r[k] = o[k] * 100;
// return accumulator of reduce
return r;
}, {})
}))
console.log(result)
You'll need to loop through the data, then for each obj, alter each arr key
let data = [{Date:"2012", A:2, B:3, C:4}, {Date:"2013", A:4, B:7, C:8}, {Date:"2014", A:0.1, B:0.3, C:0.4}]
var arr = ["A","B","C"]
data.forEach((obj) => arr.forEach((a) => obj[a] *= 100));
console.log(data);
map returns a new array, it doesn't mutate the original array it iterates over.
Array.map returns a new array, it won't mutate in place.
You could do this:
arr.forEach((element, i) => data.forEach((a,i)=>data[i].a[element]*100))
... but I wouldn't recommend it.
Mutating an object that you're iterating over is a Bad Idea, much better to map into a new array.
This is because the map function creates a new Array.
if you grab the output you will see that it is calculated and put into a new array (just with the output variables)
var data = [{Date:"2012", A:2, B:3, C:4}, {Date:"2013", A:4, B:7, C:8}, {Date:"2014", A:0.1, B:0.3, C:0.4}]
var arr = ["A","B","C"]
arr.forEach((element, i) => {
var output = data.map(a=>a[element]*100)
console.log(output);
}
)
OUTPUTS
[200, 400, 10]
[300, 700, 30]
[400, 800, 40]
https://jsfiddle.net/8ksL3pn0/
It should be like this:
arr.forEach((element, i) => data.map(a=>{a[element]*=100}))

Create a zipmap function using javascript?

Consider this problem:
Create a function zipmap that takes in two sequences, and creates a dictionary from the elements of the first sequence to the elements of the second.
zipmap([1, 2, 3], [4, 5, 6]) => {1: 4, 2: 5, 3: 6}
My solution is below as an answer, can anyone come up with a better way of doing it?
This is already built into Ramda, as zipObj:
console .log (
R.zipObj ([1, 2, 3], [4, 5, 6])
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
And it's also now a language feature, maybe not yet quite widely enough supported, but getting close: Object.fromEntries.
const zipmap = (arr1 ,arr2) => arr1.reduce((p,c,i) => {
p[c] = arr2[i];
return p;
},{});
Here's a simple recursive implementation -
// None : symbol
const None =
Symbol()
// zipMap : ('k array, 'v array) -> ('k, 'v) object
const zipMap = ([ k = None, ...keys ] = [], [ v = None, ...values ] = []) =>
k === None || v === None
? {}
: { [k]: v, ...zipMap(keys, values) }
console.log(zipMap([ 1, 2, 3 ], [ 4, 5, 6 ]))
// { 1: 4, 2: 5, 3: 6 }
But it's not much of a "mapping" function; it always returns an Object. What if you wanted a different result?
// None : symbol
const None =
Symbol()
// identity : 'a -> 'a
const identity = x =>
x
// zipMap : (('k, 'v) -> ('kk, 'vv), 'k array, 'v array) -> ('kk, 'vv) array
const zipMap =
( f = identity // ('k, v') -> ('kk, 'vv)
, [ k = None, ...keys ] = [] // 'k array
, [ v = None, ...values ] = [] // 'v array
) => // ('kk, 'vv) array
k === None || v === None
? []
: [ f ([ k, v ]), ...zipMap(f, keys, values) ]
// result : (number, number) array
const result =
zipMap
( identity
, [ 1, 2, 3 ]
, [ 4, 5, 6 ]
)
console.log(result)
// [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
console.log(Object.fromEntries(result))
// { 1: 4, 2: 5, 3: 6 }
// result2 : (number, number) array
const result2 =
zipMap
( ([ k, v ]) => [ k * 10, v * 100 ]
, [ 1, 2, 3 ]
, [ 4, 5, 6 ]
)
console.log(Object.fromEntries(result2))
// { 10: 400, 20: 500, 30: 600 }
Instead of creating an Object using Object.fromEntries, you could just as easily create a Map too -
// result2 : (number, number) array
const result2 =
zipMap
( ([ k, v ]) => [ k * 10, v * 100 ]
, [ 1, 2, 3 ]
, [ 4, 5, 6 ]
)
// m : (number, number) map
const m =
new Map(result2)
// Map { 10 => 400, 20 => 500, 30 => 600 }
const R = require('ramda')
const zipmapSeparate = (...arr) => arr[0].map((zeroEntry, index) => {
const item = {}
item[arr[0][index]] = arr[1][index]
return item
})
const zipmapReduce = (zipmap1) => zipmap1.reduce((accumulator, current) => {
const key = Object.keys(current)[0]
const value = Object.values(current)[0]
accumulator[key]=value
return accumulator
}, {})
const zipmap = R.compose(zipmapReduce, zipmapSeparate)
console.log(zipmap([1, 2, 3], [4, 5, 6]))

Is there any shorthand method to convert array of string array with header as first array to Objects of array?

Is there any shorthand method to convert array of string array with header as first array (Input as shown below) to Objects of array (as expected output shown below)
Using for loop we can achieve this, I am looking for any short hand and optimized solution to do this.
Let me know if is there any easy and optimized method to implement this.
Input
[
['fromAge', 'toAge', 'gender', 'institutionalRaf'],
[0, 10, 'F', '1.5'],
[11, 20, 'F', '2.5']
]
Expected Output :
[{
fromAge : 0,
toAge: 10,
gender: "F",
institutionalRaf : "1.5"
},
{
fromAge : 11,
toAge: 20,
gender: "F",
institutionalRaf : "2.5"
}
...
]
You can use map and reudce
Take the first element as header and rest of element as values
Loop through the values array for each element build a object with key from header and value from element
let data = [["fromAge","toAge","gender","institutionalRaf"],["1",'4','m','12'],["4",'12','f','22'],["10",'20','m','109']]
let [header,...values] = data
let final = values.map(v=> {
return v.reduce((op,inp,index)=>{
op[header[index]] = inp
return op
},{})
})
console.log(final)
You could separate the keys and the values and map the value as object with the keys.
var array = [['fromAge', 'toAge', 'gender', 'institutionalRaf'], [0, 10, 'F', '1.5'], [11, 20, 'F', '2.5']],
[keys, ...values] = array,
result = values.map(a => Object.assign(...keys.map((k, i) => ({ [k]: a[i] }))));
console.log(result);
I'd shift out the first array of keys, then .map to create entries and create the objects using Object.fromEntries:
const arr = [
['a', 'b', 'c'],
[1, 2, 3],
[4, 5, 6]
];
const keys = arr.shift();
const output = arr.map(values =>
Object.fromEntries(
values.map((value, i) => [keys[i], value])
)
);
console.log(output);
Object.fromEntries is a relatively new method. On older environments, either use a polyfill, or create the object with reduce instead:
const arr = [
['a', 'b', 'c'],
[1, 2, 3],
[4, 5, 6]
];
const keys = arr.shift();
const output = arr.map(values => (
values.reduce((a, value, i) => {
a[keys[i]] = value;
return a;
}, {})
));
console.log(output);
If keys are fixed we can use the simple approach like below
let arr=[
['fromAge', 'toAge', 'gender', 'institutionalRaf'],
[0, 10, 'F', '1.5'],
[11, 20, 'F', '2.5']
];
let arr1=arr.slice();
let x=arr1.shift();
let x1=arr1.map(a=>(
{
[x[0]]:a[0],
[x[1]]:a[1],
[x[2]]:a[2],
[x[3]]:a[3],
}
)
)
console.log(x1);
Use destructuring, map and reduce
const array = [
['fromAge', 'toAge', 'gender', 'institutionalRaf'],
[0, 10, 'F', '1.5'],
[11, 20, 'F', '2.5']
]
const [keys, ...values] = array
const result = values.map((value) => value.reduce((a, b, index) => ({...a, [keys[index]]: b}), {}), [])
console.log("result",result)

Categories

Resources