I have an array of objects
[
[{data:1},{data:2},{data:3}],
[{data:1},{data:2},{data:3}],
[{data:1},{data:2},{data:3}]
]
That I need to reduce to [{data:3},{data:6},{data:9}] via addition.
Objects of index 0 are added, objects of index 1 are added, and objects of index 2 are added.
Is there a Javascript function like reduce that can manage this?
The previous answer is good but it only works if your arrays are always the same size.
For example, having this initial input would break the code:
[
[{data:1},{data:2},{data:3}, {data:4}],
[{data:1},{data:2},{data:3}],
[{data:1},{data:2},{data:3}]
]
To fix that you could add a check inside the reducer, to make sure the next array has an object at that index.
Another problem is if you have this initial input:
[
[{data:1},{data:2},{data:3}, {data:4}],
[{data:1},{data:2},{data:3}, {data:4}, {data:5}],
[{data:1},{data:2},{data:3}]
]
The last object in the second array would be ignored because the initial reducer only takes into consideration the length of the first array.
To handle those exceptions, you could use this refactored code (based on #mickl answer):
// Initial input with different format
const initialInput = [
[{data:1},{data:2},{data:3}, {data:4}],
[{data:1},{data:2},{data:3}, {data:4}, {data:5}],
[{data:1},{data:2},{data:3}]
];
// Sort and reverse to get the array with most items first
const sortedInput = initialInput.sort().reverse();
// Finally use the refactored reducer
const result = sortedInput.reduce((arr,cur) => {
return arr.map((val, i) => {
// This "if" checks if the next array has an item on the same index of previous array
// Which is not the case, for example, from the second to the third item
if (cur[i] && cur[i].data) {
return { data: val.data + cur[i].data }
} else {
return { data: val.data}
}
})
});
console.log(result)
You can use array.reduce to aggregate the data across multiple arrays and array.map to sum up the values since it takes an arrow function where second parameter represents an index of currently processed element:
let input = [
[{data:1},{data:2},{data:3}],
[{data:1},{data:2},{data:3}],
[{data:1},{data:2},{data:3}]
];
let result = input.reduce((arr,cur) =>
arr.map((val, i) => ({ data: val.data + cur[i].data })));
console.log(result);
Related
I have an arrray as below:
var testArr = [ "1,A", "2,B", "1,D", "3,A" ]
I would like to get the result as
var result = [ "1,A", "2,B", "3,A" ]
I have tried to using filter but I cannot get the output. Could anyone please help me for this?
const testArr = ['1,A', '2,B', '1,D', '3,A'];
const result = testArr.reduce((val, cur) => {
if (!val.some((v) => v.includes(cur[0]))) {
val.push(cur);
}
return val;
}, []);
console.log(result);
var newra = oldra.filter(compare_function);
The above function 'filter' creates a new empty array and iterates through the elements in oldra. If the elements value when passed to compare_function(element_value) returns true, it will be added to the new array otherwise it will be skipped. The filter function continues iterating through the oldra building a new array as it goes until all elements are processed. The filter function then returns the new array.
The compare_function(element_value) expects the value of an element of an array to be passed to it. The programmer performs some calculation on the element_value and returns a true or false based on this calculation according to whether it should be included in the newra or not.
I'm using react-select, my api expects that i send an array of strings like this ["purple", "red", "orange"] but react-select gives me an array of objects, so what I did was map throught newValue picking each value and putting in an vaiable array, but i'm getting each value in separated arrays like this ["purple"], ["red"], ["orange"]
handleChange = (newValue: any, actionMeta: any) => {
console.log("newValue-->",newValue);
newValue.map((obj: any) => {
const array = [];
array.push(obj.value);
console.log("array-->",array);
});
complete code:
https://codesandbox.io/s/react-codesandboxer-example-sf7tz?file=/example.js
Initialize your array outside before map.
Otherwise you are creating new array for each object.
const array = [];
newValue.map((obj: any) => {
array.push(obj.value);
console.log("array-->",array);
});
As per the react select docs, it should be an array of objects with value and label. so in your case you can do in the below way.
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
let input = ["purple", "red", "orange"]
let output = input.map(item => ({value: item, label: capitalizeFirstLetter(item) }))
console.log(output)
Explanation on the Question snippet
newValue.map((obj: any) => {
const array = [];
array.push(obj.value);
console.log("array-->",array);
});
Here you are mapping through each object, then every time mapping you are creating an empty array, to that array you push the value and print it.
So take first item as example, red iterates -> empty array created -> push the red value to empty array and then printing it.
So this loops three times since the length of the array is 3, so three times you get the array.
Fix on the question snippet is declare the array outside the loop and push in the same manner as the doc suggest then you can pass to react select component as options props and it will work as expected.
i have an array of obejcts that has this structure
let events = [ {
"initDate": "2019-11-20",
"finalDate": "2019-11-22",
"intermediateDates": [
"2019-11-20",
"2019-11-21"
],
"priority": 1
},....]
So, i'm trying to get the object that matches from a given array of dates for example :
let filteredDays = [
"2019-11-20",
"2019-11-21",
"2019-11-22"
]
i'm trying with lodash like this:
let eventsFound= [];
let intersection = _.map( this.events,function(value){
let inter = _.intersection(value.intermediateDates,filteredDates);
console.log(inter);
if(inter != []){
foundEvents.push(value);
return value;
}else{
return false;
}
});
when i console log inter i get the first array with values then the next arrays are empty but it keeps pushing all the events into the foundEvents Array and the returned array is the same as the events array.
You're comparing two arrays using !=. In javascript [] != [] is always true. To check if an array is empty, use length property like arr.length == 0.
Use filter instead of using map like a forEach.
To check existance use some/includes combo instead of looking for intersections.
So, filter events that some of its intermediateDates are included in filteredDates:
let eventsFound = _.filter(this.events, event =>
_.some(event.intermediateDates, date =>
_.includes(filteredDates, date)
)
);
The same using native functions:
let eventsFound = this.events.filter(event =>
event.intermediateDates.some(date =>
filteredDates.includes(date)
)
);
In the following function I push and object to the accountsToDelete array, I need to then remove the matching object from the accountsToAdd array. I am guessing I would have to use a combination of IndexOf, Filter, Reduce but I am still a little rough in understanding how to accomplish this. This is the current function:
accountDelete(id, name) {
const accountsToAdd = this.userForm.value.accountsToAdd;
const accountsToDelete = this.userForm.value.accountsToDelete;
this.userForm.value.accountsToDelete.push(
{
id: id,
name: name
}
);
}
You can simply use the filter function. By this you can say, that in the accountToAdd all entries should be filtered, which id fits the to deleted account.
An example:
// Initialize both lists.
let accountsToAdd = []
let accountsToDelete = []
// Preparation by adding a account to the first list.
const account = { id: 1, name: 'Max' }
accountsToAdd.push(account)
// Mark account to be removed.
accountsToDelete.push(account)
accountsToAdd = accountsToAdd.filter(acc => acc.id !== account.id)
// Verify result.
console.log(accountsToAdd)
console.log(accountsToDelete)
Note:
Your both lists are defined as constant. By this you can't use the reassignment.
let's say i have this code :
let variants = []
let variant = {
variantName: 'Size',
variantItems: ['XL','MD','SM']
}
variants.push(variant)
variant = {
variantName: 'Color',
variantItems: ['Red','Blue']
}
variants.push(variant)
okay, now how do i output it to something like this :
and the variants array can contain many variant object
and if there is one more object inside variants array :
variant = {
variantName: 'Material',
variantItems: ['Plastic','Wood', 'Ceramic']
}
variants.push(variant)
and it will be outputted like this :
Please help, i very appreciate your help...
You can use a recursive function with Array#map, and Array#concat to flatten the results:
const variants = [{"variantName":"Size","variantItems":["XL","MD","SM"]},{"variantName":"Color","variantItems":["Red","Blue"]},{"variantName":"Material","variantItems":["Plastic","Wood","Ceramic"]}];
const addVariants = (variants) => {
// destructure the 1st item array, and the rest of the variants
const add = ([{ variantName, variantItems }, ...variants], row = []) =>
// iterate the variants and flatten
[].concat(...variantItems.map((variantItem) => {
// create a new array for the current row, and the variant string
const currRow = [...row, `${variantName}: ${variantItem}`];
// if there are more variants, invoke add with the remaining variants and the current row, or join the array to a string (the end result)
return variants.length ? add(variants, currRow) : currRow.join(', '); // instead of currRow.join(', ') replace with [currRow] if you want an array
}));
return add(variants);
}
const result = addVariants(variants);
console.log(result);
This is a functional ES6 approach.
let variants = [{
variantName: "Size",
variantItems: [
"XL",
"MD",
"SM"
]
},
{
variantName: "Color",
variantItems: [
"Red",
"Blue"
]
}];
let crossJoined = new Array(variants.reduce((product, variant) => (product * variant.variantItems.length), 1))
.fill(0)
.reduce(crossJoin => {
crossJoin.data.push(crossJoin.currentIndexes.map((itemIndex, variantIndex) => `${variants[variantIndex].variantName}: ${variants[variantIndex].variantItems[itemIndex]}`).join(", "));
let incrementableIndex = variants.length - crossJoin.currentIndexes
.slice()
.reverse()
.findIndex((itemIndex, variantIndex) => variants[variants.length - variantIndex - 1].variantItems.length > itemIndex + 1) - 1;
crossJoin.currentIndexes[incrementableIndex]++;
crossJoin.currentIndexes = crossJoin.currentIndexes.map((value, index) => (index > incrementableIndex
? 0
: value));
return (crossJoin.currentIndexes.length == variants.length
? crossJoin
: crossJoin.data);
}, {
data: [],
currentIndexes: new Array(variants.length).fill(0)
}).join("\n");
console.log(crossJoined);
First, an array is created with a length of all variantItems array lengths multiplied, then it is zero-filled. Next, we reduce it to another array.
crossJoin is the aggregator that holds an object of this structure most of the time:
{
data: [ … ],
currentIndexes: [ 1, 3 ] // Corresponds to variants[0].variantItems[1]
// and variants[1].variantItems[3]
}
That is “most of the time”, until the end, where currentIndexes won’t be used anymore and only its data property is returned.
So in each reduce iteration, we first push a new entry to crossJoin.data, using crossJoin.currentIndexes and the indexes of that array. Removing .join(", ") will result in an entry of the structure
[ "Size", "XL" ]
Next, we need to increment the numbers in crossJoin.currentIndexes:
[ 0, 0 ] should be incremented to [ 0, 1 ], because there is a second color at the index 1. [ 0, 1 ] should be incremented to [ 1, 0 ], because there is no third color, but a second size, but then we need the first color again, and so on. The last valid index array is [ 2, 1 ] which corresponds to the third size and the second color, which is the last combination.
incrementableIndex is the last possible index that can still be incremented. Once incremented, all subsequent indexes have to be 0.
You see those variants.length - something - 1 twice, because you need to find the first index from the end, so you have to reverse (a copy of — hence the slice) the array, then re-interpret the found index as an index from the start again.
The return returns this crossJoin object for the next iteration. The condition crossJoin.currentIndexes.length == variants.length applies to the very end, where no index can be incremented anymore. It adds a NaN to the currentIndexes array, so the lengths don’t match, and instead of filtering out or preventing the NaN, I just ignored and discarded it altogether.
The output will be an array of all combinations. You can .join("\n") it to make a string where each combination is separated by a line-break, or you can use .forEach to append each combination to a list, for example.
Use .each loop on variants array. Take another array and add object using size, color and material. So it will become combined array of your requirement. Then print new array data as per requirement using .each loop .