I am having an object that has the following structure
const arr = [
{field: "f1", values: [{ count:1, value: "a"}, { count:2, value: "b"}] },
{field: "f2", values: [{ count:3, value: "c"}, { count:4, value: "d"}] }
];
Output should look like
output = {
f1: { name: "f1", selected: [] },
f2: { name: "f2", selected: [] }
}
Basically the value in field should be key in the new object, also its name should have the same value with empty selected array
Code that I tried.
arr.map(item => {
return { item: { name: item, selected: [] } }
}
);
const arr = [
{ field: "f1", values: [{ count: 1, value: "a" }, { count: 2, value: "b" }] },
{ field: "f2", values: [{ count: 3, value: "c" }, { count: 4, value: "d" }] }
]
const output = arr.reduce((p, { field }) => {
p[field] = { name: field, selected: [] };
return p;
}, {});
console.log(output);
We can use Array.reduce() to do it
let arr = [
{ field: "f1", values: [{ count: 1, value: "a" }, { count: 2, value: "b" }] },
{ field: "f2", values: [{ count: 3, value: "c" }, { count: 4, value: "d" }] }
]
let result = arr.reduce((a,v) => {
let obj = {'name':v.field, 'selected': []}
a[v.field] = obj
return a
},{})
console.log(result)
Reduce saves an assignment but makes the code more complex to read.
forEach is better in this case
const obj = {};
arr.forEach(({field}) => obj[field] = {name:field, selected:[]})
console.log(obj)
<script>
const arr = [
{field: "f1", values: [{ count:1, value: "a"}, { count:2, value: "b"}]},
{field: "f2", values: [{ count:3, value: "c"}, { count:4, value: "d"}]}
];
</script>
Alternative is creating an object from entries generated from a map.
Still shorter than a reduce, but getting closer to it.
Still easier to see we end up with an object than go looking for the reduce initialiser.
const obj = Object.fromEntries(
arr.map(({field}) => [field, {name:field, selected:[]}])
);
console.log(obj);
<script>
const arr = [
{field: "f1", values: [{ count:1, value: "a"}, { count:2, value: "b"}]},
{field: "f2", values: [{ count:3, value: "c"}, { count:4, value: "d"}]}
];
</script>
You can use a .map() to get the field name, and a .reduce() to compose the object from the field name:
const input = [
{field: "f1", values: [{ count:1, value: "a"}, { count:2, value: "b"}] },
{field: "f2", values: [{ count:3, value: "c"}, { count:4, value: "d"}] }
];
let result = input.map(obj => obj.field).reduce((acc, name) => {
acc[name] = { name: name, selected: [] };
return acc;
}, {});
console.log(result);
Related
The current data that I have:
const data = [{ persons: [{name: "a", id: 1}], type: 1},
{ persons: [{name: "b", id: 1}], type: 2},
{ persons: [{name: "c", id: 1}], type: 3},
{ persons: [{name: "d", id: 1}], type: 4}]
What I want to achieve is:
const result = { people: [{names: ["a","b","c","d"], id: 1}], types: [1,2,3,4]}
What I have tried to do:
const result = data.reduce((result, {persons, type}) => {
for(let i = 0; i < persons.length; i++) {
if (!result.people[i]) {
result.people.push({names: [persons[i].name], id: persons[i].id]});
continue;
}
result.people[i].names.push(persons[i].name);
}
result.types.push(type);
return result
}, {people: [], types: []})
Is there perhaps a better, more elegant way to approach this?
You may use { people: {}, type: {} } in the reduce() to create a hashmap to reduce the loop.
Also, you can extract the data later with Object.vaues()
const data = [
{ persons: [{ name: "a", id: 1 }], type: 1 },
{ persons: [{ name: "b", id: 1 }], type: 2 },
{ persons: [{ name: "c", id: 1 }], type: 3 },
{ persons: [{ name: "d", id: 1 }], type: 4 },
{ persons: [{ name: "d", id: 1 }], type: 4 },
{ persons: [{ name: "d", id: 1 }], type: 4 },
{
persons: [
{ name: "d", id: 1 },
{ name: "k", id: 2 },
],
type: 4,
},
];
const groupByData = data.reduce(
(acc, cur) => {
for (const person of cur.persons) {
if (acc.people[person.id]) {
acc.people[person.id].names.push(person.name);
} else {
acc.people[person.id] = { names: [person.name], id: person.id };
}
}
if (!acc.type[cur.type]) {
acc.type[cur.type] = cur.type;
}
return acc;
},
{ people: {}, type: {} }
);
const output = {
people: Object.values(groupByData.people),
types: Object.values(groupByData.type),
};
console.log(output);
Try this:
const data = [{ persons: [{name: "a", id: 1}], type: 1},
{ persons: [{name: "b", id: 1}], type: 2},
{ persons: [{name: "c", id: 1}], type: 3},
{ persons: [{name: "d", id: 1}], type: 4}]
const people = []
const types = []
const id = data[0].persons[0].id
const result = data.reduce( (a, p) => {
people.push(p.persons[0].name)
types.push(p.type)
return{
people:[{names:people , id}] , types
}
} , data[0])
console.log(result)
I have the following Array:
const arr = [{
id: 0,
title: 'A',
countries: [{
val: "1173",
label: "England"
}, {
val: "1172",
label: "Egypt"
}],
companies: [{
val: "7346",
label: "Ab Company"
}]
},
{
id: 1,
title: 'B',
countries: [{
val: "1175",
label: "France"
}],
companies: [{
val: "8294",
label: "Cd Company"
}]
},
]
What I want to achieve is:
const arr = [{
id: 0,
title: 'A',
countries: ["England", "Egypt"],
companies: ["Ab Company"]
},
{
id: 1,
title: 'B',
countries: ["France"],
companies: ["Cd Company"]
},
]
My approach:
const mapJobArrValsToString = (arr) => {
if (!(arr && arr.length)) {
return [];
}
const fieldsToAddLabels = ['companies', 'countries'];
const clonedArr = [...arr];
clonedArr.forEach((job) => {
const objKeysList = Object.keys(job).filter((fieldName) => fieldsToAddLabels.includes(fieldName));
objKeysList.forEach((key) => {
// eslint-disable-next-line no-param-reassign
job[key] = job[key].map((el) => el.label);
});
});
return clonedArr;
};
const arr = [{
id: 0,
title: 'A',
countries: [{
val: "1173",
label: "England"
}, {
val: "1172",
label: "Egypt"
}],
companies: [{
val: "7346",
label: "Ab Company"
}]
},
{
id: 1,
title: 'B',
countries: [{
val: "1175",
label: "France"
}],
companies: [{
val: "8294",
label: "Cd Company"
}]
},
];
console.log(mapJobArrValsToString(arr));
What am I doing wrong ?
You could build new object and add this object to the copy of the object.
const
array = [{ id: 0, title: 'A', countries: [{ val: "1173", label: "England" }, { val: "1172", label: "Egypt" }], companies: [{ val: "7346", label: "Ab Company" }] }, { id: 1, title: 'B', countries: [{ val: "1175", label: "France" }], companies: [{ val: "8294", label: "Cd Company" }] } ],
keys = ['countries', 'companies'],
result = array.map(o => ({
... o,
...Object.fromEntries(keys.map(k => [k, o[k].map(({ label }) => label)]))
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do it this way too:
var arr = [{ id: 0, title: 'A', countries: [{ val: "1173", label: "England" }, { val: "1172", label: "Egypt" }], companies: [{ val: "7346", label: "Ab Company" }] }, { id: 1, title: 'B', countries: [{ val: "1175", label: "France" }], companies: [{ val: "8294", label: "Cd Company" }] }];
var keysFilter=['countries', 'companies'];
var result = arr.map((elem) => {
for (const [key, value] of Object.entries(elem)) {
if(keysFilter.includes(key)) elem[key] = value.map(val => val.label);
}
return elem;
});
console.log(result);
I hope this helps. Thanks! Happy Coding!
I'm going to be given an array of label text strings to match to values in an array. The array of objects is static but the array of text strings will be dynamic from user input. I want to know the best es6 way of setting this up using filter or map.
Here is the static array of objects:
const counts = [
{label: '0-0', value: 1},
{label: '0-1', value: 2},
{label: '0-2', value: 3},
{label: '1-0', value: 4},
{label: '1-1', value: 5},
{label: '1-2', value: 6},
{label: '2-0', value: 7},
{label: '2-1', value: 8},
{label: '2-2', value: 9},
{label: '3-0', value: 10},
{label: '3-1', value: 11},
{label: '3-2', value: 12},
];
This is an example of the array of keys I'll get:
Array [
"0-2",
"1-2",
"2-0",
"3-1"
]
So the results I want to get are an array with the values:
[3,6,7,11]
I have ways of doing this sloppily checking each value one by one, I'm just not sure what to use like filter, map, reduce, or find.
We can use filter and map function of JS for this.
let test = [
"0-2",
"1-2",
"2-0",
"3-1"
];
let counts = [{
label: '0-0',
value: 1
},
{
label: '0-1',
value: 2
},
{
label: '0-2',
value: 3
},
{
label: '1-0',
value: 4
},
{
label: '1-1',
value: 5
},
{
label: '1-2',
value: 6
},
{
label: '2-0',
value: 7
},
{
label: '2-1',
value: 8
},
{
label: '2-2',
value: 9
},
{
label: '3-0',
value: 10
},
{
label: '3-1',
value: 11
},
{
label: '3-2',
value: 12
},
];
let answer = counts.filter(item => (
test.includes(item.label)
)).map(item => item.value)
console.log(answer);
You can use array#reduce and add the matched label's value in that using array#find.
const counts = [{ label: '0-0', value: 1 }, { label: '0-1', value: 2 }, { label: '0-2', value: 3 }, { label: '1-0', value: 4 }, { label: '1-1', value: 5 }, { label: '1-2', value: 6 }, { label: '2-0', value: 7 }, { label: '2-1', value: 8 }, { label: '2-2', value: 9 }, { label: '3-0', value: 10 }, { label: '3-1', value: 11 }, { label: '3-2', value: 12 }, ],
input = [ "0-2", "1-2", "2-0", "3-1" ],
output = input.reduce((arr, label) => {
const matched = counts.find(o => o.label === label);
if(matched) arr.push(matched.value);
return arr;
},[]);
console.log(output);
You can create a Map which will act as a look-up-table (lut) for labels and their associated values. Using the Map you can then .map() your array of labels to be the value from the Map object.
By using this approach, you will only need to loop through your counts array once, and your labels array (arr) once, which will allow you to avoid "checking each value one by one", providing you with a complexity of ~ O(n + k), rather than O(nk):
const counts = [{label:"0-0",value:1},{label:"0-1",value:2},{label:"0-2",value:3},{label:"1-0",value:4},{label:"1-1",value:5},{label:"1-2",value:6},{label:"2-0",value:7},{label:"2-1",value:8},{label:"2-2",value:9},{label:"3-0",value:10},{label:"3-1",value:11},{label:"3-2",value:12}];
const arr = ["0-2", "1-2", "2-0", "3-1"];
const lut = new Map(counts.map(({label, value}) => [label, value]));
const res = arr.map(label => lut.get(label));
console.log(res);
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I have an object :
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
How can I add to each object in groups id with the same value as in name prop to get this:
const obj2 = {
app: {
groups: [{ name: 'name', values: 'values', id: 'name' }, { name: 'name1', values: 'values1', id: 'name1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values', id: 'name2' }, { name: 'name7', values: 'values4', id: 'name7' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5', id: 'name5' }, { name: 'name4', values: 'values4', id: 'name4' }]
}
};
Mutable version: If you can modify your input object, use Array.forEach() twice:
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
Object.keys(obj).forEach(key => {
obj[key].groups.forEach(x => x.id = x.name);
});
console.log(obj);
Immutable version: If you don't want to modify your input object, use Array.reduce() and the spread operator to copy properties from the inner objects:
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
const result = Object.entries(obj).reduce((acc, [key, val]) => {
acc[key] = { groups: val.groups.map(x => ({ ...x, id: x.name })) };
return acc;
}, {});
console.log(result);
In both cases, you'll need to iterate over the keys using Object.keys() or directly over the entries with Object.entries().
Loop through your object and then loop through the array in your inner object:
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
for (app in obj) {
obj[app].groups.forEach(innerObj => {
innerObj.id = innerObj.name;
});
}
console.log(obj);
Iterate over Object.keys and use map like so:
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
function addIdProp(object) {
let result = object;
Object.keys(result).forEach(key => {
result[key].groups = result[key].groups.map(({ name, values }) => { return { name, values, id: name}});
});
return result;
}
const obj2 = addIdProp(obj);
console.log(obj2);
.as-console-wrapper { max-height: 100% !important; top: 0 }
ES5 syntax:
var obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
function addIdProp(object) {
var result = object;
Object.keys(result).forEach(function(key) {
result[key].groups = result[key].groups.map(function({ name, values }) { return { name: name, values: values, id: name}});
});
return result;
}
var obj2 = addIdProp(obj);
console.log(obj2);
.as-console-wrapper { max-height: 100% !important; top: 0 }
What about with this?
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
for (let k in obj) {
if (obj.hasOwnProperty(k)) {
obj[k].groups = obj[k].groups.map((g) => {
return { ...g, id: g.name}
})
}
}
console.log(obj);
Loop through the object properties and add their group array a id prop assigning the relevant name with two nested forEaches as follows:
const obj = {
app: {
groups: [{
name: 'name',
values: 'values'
}, {
name: 'name1',
values: 'values1'
}]
},
app2: {
groups: [{
name: 'name2',
values: 'values'
}, {
name: 'name2',
values: 'values4'
}]
},
app3: {
groups: [{
name: 'name5',
values: 'values5'
}, {
name: 'name4',
values: 'values4'
}]
}
};
Object.keys(obj).forEach(e => obj[e].groups.forEach(el => el.id = el.name));
console.log(JSON.stringify(obj))
Note that it does this implementation does not create a new object, instead modifies the initial one. Your use case may welcome or discourage it.
You can try with for..in and forEach
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
for (let item in obj) {
obj[item].groups.forEach(o => o.id = o.name)
}
console.log(obj)
One solution that is purely function, immutable, and independent of scope/closure would be to reduce the [Object.entries()][1] of the obj into a new obj2 result like so:
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1' }]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
/* Reduce entries into a new object */
const obj2 = Object.entries(obj).reduce((newObject, [key, value]) => {
/* For each value of new object array of, map group items to include id : name property */
newObject[ key ] = { groups : value.groups.map(item => ({ ...item, id : item.name })) }
return newObject
}, {})
console.log(obj2)
You have to loop through the object first and then map inside the groups array like that:
const obj = {
app: {
groups: [{ name: 'name', values: 'values' }, { name: 'name1', values: 'values1'}]
},
app2: {
groups: [{ name: 'name2', values: 'values' }, { name: 'name2', values: 'values4' }]
},
app3: {
groups: [{ name: 'name5', values: 'values5' }, { name: 'name4', values: 'values4' }]
}
};
var newObj = {...obj};
for(var key in newObj){
newObj[key] = newObj[key].groups.map(item => ({...item, id: item.name}));
}
console.log(newObj); // here is your new object with id's
You can do:
const obj = {app: {groups: [{name: 'name',values: 'values'}, {name: 'name1',values: 'values1'}]},app2: {groups: [{name: 'name2',values: 'values'}, {name: 'name2',values: 'values4'}]},app3: {groups: [{name: 'name5',values: 'values5'}, {name: 'name4',values: 'values4'}]}};
const obj2 = Object.keys(obj).map(k => ({
[k]: {
groups: obj[k].groups.map(o => {
o.id = o.name;
return o;
})
}
}));
console.log(obj2);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have such an object
data: {
dataFirst: {
Food: [ {id: 536131, name: "option1", }]
},
dataSecond: {
Autos: [{id: 678, name: 'option1'}],
Houses: [
{id: 6876, name: "option1"},
{id: 6876, name: "Placed App"},
],
Phones: [
{id: 672, name: "option1"},
{id: 97249, name: "Placed},
],
Food: [
{id: 772, name: "option1"},
{id: 6777, name: "Placed},
],
}
}
The problem is, that I may have same data in dataFirst and dataSecond, for examle 'Food', I have 2 array objects that contains different data but I need to make it one object 'Food' with the data from 2 of them, from the dataFirst 'Food' and dataSecond 'Food'. I had such a code:
export const parser = ({ data }) => {
const result = Object.values(data).reduce((prev, topicsGroup) => {
Object.assign(prev, topicsGroup);
return prev;
}, {});
return result;
}
but this code doesn't unite 2 'Food' objects but returns data only from the dataFirst 'Food' object wihout second one.
You can iterate through all values of your main data object with Object.values(data) and combine them with reduce by concatenating arrays corresponding to common keys:
let data = {
dataFirst: {
Food: [{
id: 536131,
name: "option1",
}]
},
dataSecond: {
Autos: [{
topicId: 678,
name: 'option1'
}],
Houses: [{
topicId: 6876,
name: "option1"
},
{
topicId: 6876,
topicName: "Placed App"
},
],
Phones: [{
topicId: 672,
name: "option1"
},
{
topicId: 97249,
name: "Placed"
},
],
Food: [{
topicId: 772,
name: "option1"
},
{
topicId: 6777,
name: "Placed"
},
],
}
};
let res = Object.values(data).reduce((acc, curr) => {
Object.entries(curr).forEach(([k, v]) => {
if (k in acc) acc[k] = acc[k].concat(v);
else acc[k] = v;
});
return acc;
}, {});
console.log(res);