Replace map() with reduce() function - javascript

I try to replace map to reduce but without success. Can you help me rewrite this piese of code:
this.tares = this.tares
.map(tare => {
let suppliers = [];
this.organizations.forEach(organization => {
if (organization.tare_ids.indexOf(tare.id) !== -1) {
suppliers = suppliers.concat(tare.suppliers);
}
});
return { ...tare, suppliers };
});
My supplier variable as an accumulator parameter of a reduce function but in this case, I can't figure out how to apply reduce. Nested arrays crashing me.

You could take a Set for all id and map the array with an object and a check if id exists.
var ids = this.organizations.reduce(
(s, { tare_ids }) => tare_ids.reduce((t, id) => t.add(id), s),
new Set
);
this.tares = this.tares.map(tare => ({ ...tare, suppliers: ids.has(tare.id) ? tare.suppliers : [] }));

Related

How to get data inside a Nested Array of object

const datafromback=[[{name:ravi}],[{}],[{}],[{}]]
I want to access ravi. Can anyone help me how can i see ravi in my console.with dealing with nested arrays
I not getting approach but i can use map to map through datafromback array but don't know how to get inside it
You can use 0 index
const datafromback = [[{ name: 'ravi' }], [{}], [{}], [{}]]
const dataFrom = (arr, nameRavi) => {
let result
arr.forEach((ele) => {
if (ele[0].name === nameRavi) result = ele[0].name
})
return result
}
console.log(dataFrom(datafromback, 'ravi'))
one possible way is to use flat first to remove nested array
const datafromback=[[{name:ravi}],[{}],[{}],[{}]]
const flatternArray = datafromback.flat() // [{name:ravi},{},{},{}]
flatternArray.map(item => {
console.log(item.name) //
})
you can do this :
const datafromback=[[{name:'ravi'}],[{}],[{}],[{}]]
const [{name}] = datafromback.find(data=>data.find(item=>item.name === 'ravi')?.name === 'ravi')
console.log(name)
You can create a recursive function if you have non-fixed dimensions array :
const handle = e => {
if (Array.isArray(e))
return e.map(handle)
else {
console.log(e.name)
}
}
handle(array)
Or if you know the dimensions, you can use nested for loops like so :
// example for 2 dimensions
for (let y = 0; y < array.length; y++)
for (let x = 0; x < array[y].length; x++)
console.log(array[y][x].name)
Hey so what you have above is a 2D object array. if you just want to console you can use a nested forEach to get the key value, like this.
datafromback.forEach(data => {
//this is the nested array that contains the objects
data.forEach(obj => {
//here you can access the actual object
if (obj?.name) console.log(obj.name);
});
});
this will return the value of key (name) if present ie ravi in your case.
you can do it like this
const datafromback = [[{ name: 'ravi' }], [{}], [{}], [{}]];
const names = [];
datafromback.map((items) => {
items.map((item) => {
if (item?.name) names.push(item?.name);
});
});
console.log(names);

How do construct ForEach into map in javascript

I have sample JSON in form of
var sample=[{"id":200,"children":[{"value":300,"type":"SINGLE"},{"value":400,"type":"CLASSIC"},{"value":600,"type":"DUAL"}]},{"id":300,"children":[{"value":500,"type":"TRIO"},{"value":600,"type":"MUSICAL"},{"value":700,"type":"UMBRELA"}]}]
var result = [];
sample.forEach(function(e){
let obj = {}
obj.id=e.id
obj['somekey']=e.children[0].value
obj['someanotherkey']=e.children[1].type
result.push(obj);
})
console.log(result)
How do i can achieve same using map es-6
var sample=[{"id":200,"children":[{"value":300,"type":"SINGLE"},{"value":400,"type":"CLASSIC"},{"value":600,"type":"DUAL"}]},{"id":300,"children":[{"value":500,"type":"TRIO"},{"value":600,"type":"MUSICAL"},{"value":700,"type":"UMBRELA"}]}]
var output = sample.map(({ id, children }) => ({ id, ...children[0] }));
console.log(output);
.map() returns an array, so you must set up a variable to hold that result. Then, within the loop, you use return to effectively push items into the array.
var sample=[{"id":200,"children":[{"value":300,"type":"SINGLE"},{"value":400,"type":"CLASSIC"},{"value":600,"type":"DUAL"}]},{"id":300,"children":[{"value":500,"type":"TRIO"},{"value":600,"type":"MUSICAL"},{"value":700,"type":"UMBRELA"}]}];
let result = sample.map(function(e){
let obj = {}
obj.id=e.id;
obj['value']=e.children[0].value;
obj['type']=e.children[0].type
return obj;
});
console.log(result);
If you want to be able to chose the children index:
const getDataChild = (a, i) => a.map(({id, children:ch}) => ({id, ...ch[i]}));
console.log(getDataChild(sample, 0)); // where 0 is the desired index

JS: Don't add value to map on condition

I have an object that I need to convert into an array. This is the object:
const dogCounts: {
maltese: 4,
poodle: 2,
labrador: 10,
corso: 0
}
And I send it to a component thru props and I have a useMemo hook to convert it into a structure like this: [ [maltese, 4], [poodle, 2], ... ]
const formatDogCounts = useMemo(() => {
const z = Object.keys(dogCounts || {})?.map(i => {
if (dogCounts[i] === 0) return; // Don't add it to map and skip it
return [i, dogCounts[i]]
})
}, [dogCounts])
When the number is zero I don't want to add it to the formatDogCounts variable. What I put above doesn't fit to eslints rules. Arrow function expected no return value.eslintconsistent-return.
Also I put that {} in the object.keys for the case when the counts haven't loaded yet is there a cleaner way to null check that?
map doesn't filter out values; doing a simple return; in map makes the corresponding entry in the result array undefined. If you want to do that, you'll need to filter first, or build the array another way (such as a simple loop).
Here's the filter approach:
const formatDogCounts = useMemo(() => {
const z = Object.keys(dogCounts || {})?
.filter(name => dogCounts[name] !== 0)
.map(name => [name, dogCounts[i]]);
}, [dogCounts]);
Note that Object.entries provides the very [name, value] pairs you want, so you could avoid map, and there's no reason for the conditional chaining operator as neither Object.keys nor Object.entries ever returns undefined or null:
const formatDogCounts = useMemo(() => {
const z = Object.entries(dogCounts || {})
.filter(([, value]) => value !== 0);
}, [dogCounts]);
Note the , prior to value in the [, value] destructuring pattern so we're grabbing the second array entry (the value), not the first (the name).
We can also avoid the calls to Object.entries and filter entirely when there is no dogCounts:
const formatDogCounts = useMemo(() => {
const z = dogCounts
? Object.entries(dogCounts).filter(([, value]) => value !== 0)
: [];
}, [dogCounts]);
In a comment you've said:
The entries solution worked really well for me! Is there a way now to return an object instead of an array with total dog counts and then an array of items? Ex: formatDogCounts: { totalDogs: 30, items: [...] }
Sure. If there will only ever be a reasonable number of dogs (fewer than hundreds of thousands), I'd just do it as a separate operation at the end:
const formatDogCounts = useMemo(() => {
const items = dogCounts
? Object.entries(dogCounts).filter(([, value]) => value !== 0)
: [];
return {
totalDogs: items.reduce((sum, [, value]) => sum + value, 0),
items,
};
}, [dogCounts]);
(A straight sum is the only ad hoc use of reduce I'll do, and even then I don't much care for it.)
Or you could make your filter callback slightly impure and count them as you go:
const formatDogCounts = useMemo(() => {
let totalDogs = 0;
const items = dogCounts
? Object.entries(dogCounts).filter(([, value]) => {
totalDogs += value;
return value !== 0;
})
: [];
return {
totalDogs,
items,
};
}, [dogCounts]);
If you want to perform a map and filter operation together, you can use flatMap, returning an empty array to skip an element.
const formatDogCounts = useMemo(() => {
const z = Object.keys(dogCounts || {})?.flatMap(i => {
if(dogCounts[i] === 0) return []; // Dont add it to map and skip it
return [[i, dogCounts[i]]];
})
}, [dogCounts])
You are trying to filter using a map which isn't possible. Map will return the same amount of values as you put in. You can do a foreach or you can do a combination of map and filter to get the expected results.
Foreach
const z = []
Object.keys(dogCounts).forEach((key) => {
if(dogCounts[key]) {
// if the value is truthy push the structure to the array.
z.push([key, dogCounts[key]]);
}
}
Map/Filter
const z = Object.keys(dogCount)
.map((key) => [key, dogCount[key]) // map to restructure object.keys
.filter(([key, value]) => value); // filter to remove falsey values (0, null, undefined)

ES6 Filtering objects by unique attribute

I've an array of errors, each error has a non-unique param attribute.
I'd like to filter the array based on whether the param has been seen before.
Something like this:
const filteredErrors = [];
let params = [];
for(let x = 0; x < errors.length; x++) {
if(!params.includes(errors[x].param)) {
params.push(errors[x].param);
filteredErrors.push(errors[x]);
}
}
But I've no idea how to do this in ES6.
I can get the unique params const filteredParams = Array.from(new Set(errors.map(error => error.param)));
but not the objects themselves.
Pretty sure this is just a weakness in my understanding of higher order functions, but I just can't grasp it
You could destrucure param, check against params and add the value to params and return true for getting the object as filtering result.
As result you get an array of first found errors of the same type.
const
params = [],
filteredErrors = errors.filter(({ param }) =>
!params.includes(param) && params.push(param));
Instead of an array you can make use of an object to keep a map of existing values and make use of filter function
let params = {};
const filteredErrors = errors.filter(error => {
if(params[error.param]) return false;
params[error.param] = true;
return true;
});
i'd probably do it like this with a reduce and no need for outside parameters:
const filteredErrors = Object.values(
errors.reduce((acc, val) => {
if (!acc[val.param]) {
acc[val.param] = val;
}
return acc;
}, {}))
basically convert it into an object keyed by the param with the object as values, only setting the key if it hasn't been set before, then back into an array of the values.
generalized like so
function uniqueBy(array, prop) {
return Object.values(
array.reduce((acc, val) => {
if (!acc[val[prop]]) {
acc[val[prop]] = val;
}
return acc;
}, {}))
}
then just do:
const filteredErrors = uniqueBy(errors, 'param');
If your param has a flag identifier if this param has been seen before then you can simply do this.
const filteredErrors = errors.filter(({ param }) => param.seen === true);
OR
const filteredErrors = errors.filter((error) => error.param.seen);
errors should be an array of objects.
where param is one of the fields of the element of array errors and seen is one of the fields of param object.
You can do it by using Array.prototype.reduce. You need to iterate through the objects in the array and keep the found params in a Set if it is not already there.
The Set.prototype.has will let you find that out. If it is not present in the Set you add it both in the Set instance and the final accumulated array, so that in the next iteration if the param is present in your Set you don't include that object:
const errors = [{param: 1, val: "err1"}, {param: 2, val: "err2"}, {param: 3, val: "err3"}, {param: 2, val: "err4"}, {param: 1, val: "err5"}];
const { filteredParams } = errors.reduce((acc, e) => {
!acc.foundParams.has(e.param) && (acc.foundParams.add(e.param) &&
acc.filteredParams.push(e));
return acc;
}, {foundParams: new Set(), filteredParams: []});
console.log(filteredParams);

using reduce to remove item from array

I need a bit of help to remove items from an array. I have a number of check boxes, each have a dynamically created data attribute. On un-checking a check box I want to remove items from an array which match item.sector value of each array item. I can't quite get it working. Any help would be appreciated.
let mapMarkers = [];
function addFilter(self) {
const markerObject = filterObject[self.id];
const markerID = markerObject[0]["id"];
const dataSetSector = self.dataset.sector;
const dataSetYear = self.dataset.year;
if (self.checked) {
mapMarkers.push(markerObject);
} else {
// data attribute SECTOR exists
if (self.hasAttribute("data-sector")) {
mapMarkers = mapMarkers.reduce((acc, curr) => {
if (curr.sector !== dataSetSector) acc.push(curr);
return acc;
});
}
// data attribute YEAR exists
else if (self.hasAttribute("data-year")) {
mapMarkers = mapMarkers.reduce((acc, curr) => {
if (curr.sector !== dataSetYear) acc.push(curr);
return acc;
});
}
}
}
The second argument of reduce is the initial value, i.e. the initial value of acc. Since you're calling a push method on acc, it should probably be an array.
When you omit the second argument, the first element in your array is used as the initial value. I.e.: the first call takes (mapMarkers[0], mapMarkers[1]).
To fix this issue, pass a new array to the reduce method:
mapMarkers = mapMarkers.reduce((acc, curr) => {
if (curr.sector !== dataSetSector) acc.push(curr);
return acc;
}, []);
// ^--- change
Alternatively, as suggested in the comments, you can use filter.

Categories