Using reduce on an object with an array - javascript

I have this JavaScript object
{
names: [ "youtube","twitch"],
autoEnable: true
}
I want to convert this object into an array of objects for every name I have in my array. autoEnable and isEnabled correspond
[{
name: youtube,
isEnabled: true
},
{
name: twitch,
isEnabled: true
}]

This isn't a use case for reduce (outside of functional programming with prebuilt, reusable reducers, almost nothing is, though it's often used anyway); it's a case for map:
const obj = {
names: [ "youtube","twitch"],
autoEnable: true
};
const array = obj.names.map(name => ({name, views: 46, isEnabled: obj.autoEnable}));
Live Example:
const obj = {
names: [ "youtube","twitch"],
autoEnable: true
};
const array = obj.names.map(name => ({name, views: 46, isEnabled: obj.autoEnable}));
console.log(array);

const data = {
names: [ "youtube","twitch"],
autoEnable: true
}
const a = data.names.map((name) => {
return {
name,
isEnabled: true, // you could run a function here to get this value
views: 46 // you could run a function here to get this value
}
});
console.log(a);

Related

How to reduce a property within an array of array?

I have an array of products which in turn have an array of categories. I want to extract distinct values of the type property on category object.
Both the Lodash and native versions below do the job.
I want to make a generic function which takes the path of property and return unique values.
Essentially I am looking at something terse like
map(products, property("categories[].type") but heres the longer version(s)
import { compact, flatten, map, property, uniq } from "lodash";
export const getAllTypes1 = (products) => {
return uniq(
compact(map(flatten(map(products, property("categories"))), "type"))
);
};
export const getAllTypes2 = (products) => {
const types = [];
products.forEach((product) => {
product.categories.forEach((category) => {
if (!types.some((t) => t === category.type)) {
types.push(category.type);
}
});
});
return types;
};
Example data
const product1 = {
name: 'Wilson Orange',
price: 72.50,
categories: [{
type: 'flash sale',
discountable: false,
},{
type: 'tennis',
discountable: true,
}]
};
const product2 = {
name: 'Babolat Green',
price: 65.50,
categories: [{
type: 'tennis',
discountable: true,
}]
};
const products = [product1, product2];
Result
const result = getAllTypes2(products);
console.log(result); // ["flash sale", "tennis"]
Here's a working example
Here's a vanilla JS function that takes the path without needing [] and automatically checks arrays wherever it finds one.
How it works is:
Create an empty Set to easily remove duplicates
Turn the path string to an array of properties -> props
Call a recursive function recurse(currObj, props) which:
Checks if the currObj is an array, and if it is:
a. Recurses again with the array values as currObj
b. Use the same props since we didn't check an object in the path
Check if we're at the last prop in the path, if yes
a. Add the property's value in the current object to the set
Otherwise. recurse with currObj[currProp], and the remaining props
Convert the set to an array and return it.
const product1 = {
name: 'Wilson Orange',
price: 72.5,
categories: [
{
type: 'flash sale',
discountable: false,
},
{
type: 'tennis',
discountable: true,
},
],
};
const product2 = {
name: 'Babolat Green',
price: 65.5,
categories: [
{
type: 'tennis',
discountable: true,
},
],
};
const products = [product1, product2];
function getProperties(array, path) {
const props = path.split('.');
const values = new Set();
function recurse(currObj, props) {
const currProp = props[0]
const nextProps = props.slice(1);
if (Array.isArray(currObj)) {
for (let val of currObj) {
recurse(val, props);
}
return
}
if (nextProps.length === 0) {
values.add(currObj[currProp])
} else {
recurse(currObj[currProp], nextProps)
}
}
recurse(array, props);
return [...values];
}
console.log(getProperties(products,'categories.type'))
console.log(getProperties(products,'price'))
console.log(getProperties(products,'name'))
It's not a property path string, but it's pretty terse and expressive:
const pipe = (...fs) => fs.reduceRight(
(next, f) => x => f(x, next), x => x,
);
const getAllTypes = pipe(
(x, next) => [...new Set(x.flatMap(next))],
(x, next) => x.categories.map(next),
(x) => x.type,
);
// equivalent to
// const getAllTypes =
// x => [...new Set(x.flatMap(y => y.categories.map(z => z.type)))];
const products = [{
name: 'Wilson Orange',
price: 72.50,
categories: [{
type: 'flash sale',
discountable: false,
}, {
type: 'tennis',
discountable: true,
}]
}, {
name: 'Babolat Green',
price: 65.50,
categories: [{
type: 'tennis',
discountable: true,
}]
}];
console.log(getAllTypes(products));
The parameter x => x allows pipe to be called without any arguments and return the identity function. It also allows the last function argument of pipe to accept a next parameter for consistency, i.e. (x, next) => next(x.type) would have been equivalent to (x) => x.type.
Reference:
Array.prototype.flatMap
Array.prototype.map
Array.prototype.reduceRight
Set

How to get only true value key pair from array of objects

I have an array of object that is coming from some api.The data i am getting is like this. It has multiple values but i only want to show the user which access he has. Suppose a user have only read access so i want to show the user read key.
[
{
admin: false,
createdAt: "2022-08-21T05:32:20.936Z",
id: 8,
read: false,
write: true,
},
];
So, i want to get only the key value pair from this array which has true values so i can show the keys using Object.keys().
expected result
[{write:true}]
I have tried different array methods but didn't succeed, here how i was thinking to solve this problem but it's only returning last element value.
item.map(tab => {
return Object.keys(tab).reduce((acc: string, key) => {
if (tab[key]) {
acc[key] = tab[key];
}
return acc;
}, {});
}),
You can get rid of reduce by creating an object from filtered entries. Then just filter by true values.
data = [
{
admin: false,
createdAt: "2022-08-21T05:32:20.936Z",
id: 8,
read: false,
write: true,
},
{
admin: false,
createdAt: "1234",
id: 8,
read: true,
write: true,
}
];
out = data.map(item => Object.fromEntries(Object.entries(item).filter(([key, value]) => value === true)));
console.log(out)
if (tab[key]) will be applied on any truthy value not just true, for example, not empty string is a truthy value, any number is a truthy value except zero.
So you need explicitly check if the value equal to true by if (tab[key] === true)
const data = [
{
admin: false,
createdAt: "2022-08-21T05:32:20.936Z",
id: 8,
read: false,
write: true,
},
];
const result = data.map(tab => {
return Object.keys(tab).reduce((acc, key) => {
if (tab[key] === true) {
acc[key] = tab[key];
}
return acc;
}, {});
})
console.log(result)
For shorthand use can use
const result = data.map(tab => Object.entries(tab).reduce((acc, [key, value]) => ({ ...acc, ...(value === true && { [key]: value }) }), {}));
You can get the keys you want by changing the 2nd parameter of the keyFilters function.
let tabs = [
{admin: false,createdAt: "2022-08-21T05:32:20.936Z",id: 8,read: false,write: true},
{admin: false,createdAt: "2022-08-21T05:32:20.936Z",id: 8,read: true,write: true}
];
let keyFilters = function(values, keys){
let filteredKeys = {}
Object.keys(values).map((key, index)=>{
if (keys.includes(key)){
filteredKeys[key] = values[key]
}
})
return filteredKeys;
}
let filters = tabs.map(tab=>keyFilters(tab, ["read", "write"]))
console.log(filters)
output
0:(2) {read: false, write: true}
1:(2) {read: true, write: true

Sort an array of objects dynamically javascript

I am having an array with the below items. I need to sort the below array to the array that is shown in sorted items,so that all the value with the rules can be together and the ELIG_DATABASE should be grouped with the ELIG_SERVICE.
const items =[{"name":"ELIG_DATABASE","ready":true},
{"name":"ELIG_RULES_SERVICE","ready":true},
{"name":"ELIG_GATEWAY","ready":true},
{"name":"ELIG_GATEWAY_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE","ready":true},
{"name":"HDXTS","ready":false},
{"name":"RULES_VERSION","ready":true},];
I want to achieve this array so that values in the name property that has rules can be together,gateway things should be together, elig service thing should be together just that ELIG_DATABASE should be grouped together with elig service and then all other values in the name property can be sorted alphabetically.
const sortedItems =[
{"name":"ELIG_GATEWAY","ready":true},
{"name":"ELIG_GATEWAY_LATEST","ready":true,"latest":true},
{"name":"ELIG_RULES_SERVICE","ready":true},
{"name":"RULES_VERSION","ready":true},
{"name":"ELIG_DATABASE","ready":true},
{"name":"ELIG_SERVICE_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE","ready":true},
{"name":"HDXTS","ready":false}
];
I tried using this code but that sorts alphabetically putting ELIG_DATABASE in first position.Could any one please help on how to achieve this array in minimum code as possible.
items.sort((svcA, svcB) => {
const serviceA = svcA.name.toUpperCase();
const serviceB = svcB.name.toUpperCase();
return serviceA.localeCompare(serviceB);
});
You could take the wanted groups first in an array, sort the data and assign the object to the group or to the end of a temp array and get the flat data as result.
var data = [{ name: "ELIG_DATABASE", ready: true }, { name: "ELIG_RULES_SERVICE", ready: true }, { name: "ELIG_GATEWAY", ready: true }, { name: "ELIG_GATEWAY_LATEST", ready: true, latest: true }, { name: "ELIG_SERVICE_LATEST", ready: true, latest: true }, { name: "ELIG_SERVICE", ready: true }, { name: "HDXTS", ready: false }, { name: "RULES_VERSION", ready: true }],
together = [['GATEWAY'], ['RULES'], ['ELIG_DATABASE', 'ELIG_SERVICE']],
groups = { GATEWAY: [], RULES: [], ELIG_DATABASE: [] },
temp = [groups.GATEWAY, groups.RULES, groups.ELIG_DATABASE],
result;
for (let o of data.sort(({ name: a }, { name: b }) => a.localeCompare(b))) {
let target = together.find(a => a.some(v => o.name.includes(v)));
if (target) groups[target[0]].push(o);
else temp.push(o);
}
result = temp.flat();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JS/TS how to iterate thru object array and change values

consider the following array.
routingButtonsHighlighter = [
{vehicle: true},
{userAssignment: false},
{relations: false}
];
What is the best way to build a function which can do the following goals?
1) will set all members to false
2) set chosen member to true ( passed as a parameter )
Absent more specific requirements, this is a bit of a choose-your-own-adventure.
(Note: For brevity this code uses ES6 computed property names and destructuring assignment and ES2018 object spread syntax, all of which can be transpiled by TypeScript.)
If each object has exactly one key
...and you want to mutate the original array and objects
const objects = [ { vehicle: true }, { userAssignment: false }, { relations: false } ];
function selectKey(objects, selectedKey) {
for (let obj of objects) {
const [key] = Object.keys(obj);
obj[key] = key === selectedKey;
}
return objects;
}
selectKey(objects, 'userAssignment');
console.log(objects);
...and you want a new array of new objects
const objects = [ { vehicle: true }, { userAssignment: false }, { relations: false } ];
function selectKey(objects, selectedKey) {
const newObjects = [];
for (let obj of objects) {
const [key] = Object.keys(obj);
newObjects.push({ [key]: key === selectedKey });
}
return newObjects;
}
console.log(selectKey(objects, 'userAssignment'))
...but you really like functional style
const objects = [ { vehicle: true }, { userAssignment: false }, { relations: false } ];
function selectKey(objects, selectedKey) {
return objects.map(obj => {
const [key] = Object.keys(obj);
return { [key]: key === selectedKey };
});
}
console.log(selectKey(objects, 'userAssignment'))
If the objects can have more than one key
...and you want to mutate the original array and objects
const objects = [
{ vehicle: true, relations: false },
{ userAssignment: false, vehicle: true },
{ relations: false, userAssignment: false },
];
function selectKey(objects, selectedKey) {
for (let obj of objects) {
for (let key of Object.keys(obj)) {
obj[key] = key === selectedKey;
}
}
return objects;
}
selectKey(objects, 'userAssignment');
console.log(objects);
...and you want a new array of new objects
const objects = [
{ vehicle: true, relations: false },
{ userAssignment: false, vehicle: true },
{ relations: false, userAssignment: false },
];
function selectKey(objects, selectedKey) {
const newObjects = [];
for (let obj of objects) {
const newObj = {};
for (let key of Object.keys(obj)) {
newObj[key] = key === selectedKey;
}
newObjects.push(newObj);
}
return newObjects;
}
console.log(selectKey(objects, 'userAssignment'))
...but you really like functional style
const objects = [
{ vehicle: true, relations: false },
{ userAssignment: false, vehicle: true },
{ relations: false, userAssignment: false },
];
function selectKey(objects, selectedKey) {
return objects.map(obj =>
Object.keys(obj).reduce((newObj, key) =>
({ ...newObj, [key]: key === selectedKey }),
{}
)
);
}
console.log(selectKey(objects, 'userAssignment'))
You can iterate the array with Array.forEach(), get the key using Object.keys(), compare to the selected key, and set the value accordingly:
const routingButtonsHighlighter = [{vehicle: true}, {userAssignment: false}, {relations: false}];
const select = (arr, selectedKey) =>
arr.forEach((o) => {
const key = Object.keys(o)[0];
o[key] = key === selectedKey;
});
select(routingButtonsHighlighter, 'userAssignment');
console.log(routingButtonsHighlighter);
Creating a method for something like this would be highly specialized, so to abstract it, I've decided to write it like this:
function arrayFlagSinglePropertyTrue(key, arrayofobjects) {
for (let i in arrayofobjects) {
let keys = Object.keys(arrayofobjects[i]);
if (keys[0] == key) {
arrayofobjects[i][keys[0]] = true;
} else {
arrayofobjects[i][keys[0]] = false;
}
}
return arrayofobjects;
}
routingButtonsHighlighter = [
{vehicle: true},
{userAssignment: false},
{relations: false}
];
console.log(arrayFlagSinglePropertyTrue("relations", routingButtonsHighlighter));
Although this will get what you require done, its highly specialized and only works if the objects in the array contain one property or at the very least the first property in the object itself is the one you want to set to flag.
Edit: Some advice:
Uniformity in lists helps avoid the issue you have. By structuring your objects with uniform property names and then acting on the values themselves, you no longer require the use of specialized functions or code in order to modify it. At this point you can rely on fundamental programming logic to change the properties efficiently.
If you get the list from some external source and have no control over it, then you may need to either reorganize it yourself. If you can't then making specialized functions/codes is your last resort.
If possible, take something like this:
routingButtonsHighlighter = [
{vehicle: true},
{userAssignment: false},
{relations: false}
];
Organize it into something like this where the actual object properties are uniform:
let betterStructureObject = [
{ propertyName: "vehicle", status: true },
{ propertyName: "userAssignment", status: false },
{ propertyName: "vehicle", status: false },
]
So you can easily loop over it and not have to worry about writing specialized code.
for (let i in betterStructureObject) {
if (betterStructureObject[i].propertyName == "vehicle")
betterStructureObject[i].status = true;
else betterStructureObject[i].status = false;
}

Building adjacency matrix from an array of objects

I have a plain JavaScript array of objects, say e.g.
const drawings = [
{
name: "Foo",
category: "widget"
},
{
name: "Bar",
category: "widget"
},
{
name: "Bar",
category: "fidget"
},
]
etc, where both the name and category have duplicates. What I want to end up with is essentially a list of objects (this is to meet the interface for a 3rd party library), where each object represents a name, and then for each category there is a property that is either true or false, depending on the original list. So for the example the output would be:
const output = [
{
name: "Foo",
widget: true,
fidget: false
},
{
{
name: "Bar",
widget: true,
fidget: true
},
]
I would first go through and make an object of your categories with the categories as keys and default values as false.
Then you can assign this to each object and set the correct keys to true as you go through.
const drawings = [{name: "Foo",category: "widget"},{name: "Bar",category: "widget"},{name: "Bar",category: "fidget"},]
// make category object where everything is false
let category_obj = drawings.reduce((a, item) => (a[item.category] = false, a), {})
let output = drawings.reduce((a, {name, category}) => {
// assign cat
if (!a.hasOwnProperty(name)) a[name] = Object.assign({}, {name}, category_obj)
// set to true if the correct category
a[name][category] = true
return a
}, {})
// the above makes an object, but you only want the array of values
console.log(Object.values(output))
If you already know the categories or if you have infered them as you suggested, you could use Array.reduce() like such:
drawings.reduce(function(acc, curr) {
if (!acc.some(elt => elt.name === curr.name)) {
acc.push({name: curr.name, widget: false, fidget: false})
}
const i = acc.findIndex(elt => elt.name === curr.name)
acc[i][curr.category] = true
return acc
}, [])

Categories