Array with Arrays that have objects -> format to string - javascript

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!

Related

Convert nested aray into group of objects: Javacript

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

How to check if name matches in both items of the array

I'm trying to figure out how you would return the matching value in this example below.
const selected = [{
name: 'Colour group',
value: 'White',
}, {
name: 'Style',
value: 'Cream',
}];
const overrides = [{
facets: [{
name: "Colour group",
values: ["White"]
}]
}, {
facets: [{
test: 'name',
name: "Colour group",
values: ["White", "Cream"],
}]
}, {
facets: [{
test: 'name',
name: "Colour group",
values: ["White", "test"],
}]
}, {
facets: [{
name: "Colour group",
values: ["White"]
}, {
name: "Style",
values: ["Cream"]
}]
}, {
facets: [{
name: "Colour group",
values: ["White"]
}, {
name: "Range",
values: ["Cream"]
}]
}];
So if my selected facets were Colour group and Style I'd then need to match it to the override that also has Colour group and Style and return. Ignoring the one that has Colour group and Range.
I have a JSfiddle with my current progress:
https://jsfiddle.net/61wvubmz/53/
Ideally this will then check if the values are the same and return the override object.
The expected return would be
{
facets: [{
name: "Colour group",
values: ["White"]
}, {
name: "Style",
values: ["Cream"]
}]
}
from the override array
const lookfor = selected.map((val) => val.name);
const facets = overrides.filter((o) => {
let compatible = true;
lookfor.forEach((lf) => {
if(!o.facets.find((of) => of.name === lf)){
compatible = false
}
})
return compatible
})
console.log(facets);
https://jsfiddle.net/r5nd40z9/4/
const selected = [{
name: 'Colour group',
value: 'White',
},
{
name: 'Style',
value: 'Cream',
}
];
const overrides = [{
facets: [{
name: "Colour group",
values: ["White"]
}]
},
{
facets: [{
test: 'name',
name: "Colour group",
values: ["White", "Cream"],
}]
}, {
facets: [{
test: 'name',
name: "Colour group",
values: ["White", "test"],
}]
}, {
facets: [{
name: "Colour group",
values: ["White"]
}, {
name: "Style",
values: ["Cream"]
}]
}, {
facets: [{
name: "Colour group",
values: ["White"]
}, {
name: "Range",
values: ["Cream"]
}]
}];
let result = {}
let output = overrides.filter(o1 => !selected.some(s1 => {
let r1, r2;
r1 = o1.facets[0].name === s1.name;
(o1.facets.length > 1) ? r2 = o1.facets[1].name === s1.name : ' ';
r2 ? result = o1 : ' '
}));
console.log(result)

How to get unique array from nested object with lodash

My collection contains the following (array of objects):
[
{
id: 'id-1',
uniqueName: 'operation-level-1',
operations: [
{
name: 'operaion-1',
label: 'operation-1-label'
},
{
name: 'operaion-2',
label: 'operation-2-label'
}
]
},
{
id: 'id-2',
uniqueName: 'operation-level-2'
operations: [
{
name: 'operaion-1',
label: 'operation-1-label'
},
{
name: 'operaion-3',
label: 'operation-3-label'
}
]
}
]
I wanted to get an array of unique operation name and label as shown below
const result = [
{
name: 'operaion-1',
label: 'operation-1-label'
},
{
name: 'operaion-2',
label: 'operation-2-label'
},
{
name: 'operaion-3',
label: 'operation-3-label'
}
]
Can someone suggest the best way to achieve this, please?
This can be easily done without lodash. You can first flat the data then map it as a key value pair and then make use of Map to remove the duplicate entries:
var data=[{ id: 'id-1', uniqueName: 'operation-level-1', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-2', label: 'operation-2-label' } ] }, { id: 'id-2', uniqueName: 'operation-level-2', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-3', label: 'operation-3-label' } ] }];
var result = [...new Map(data.flatMap(({operations})=>operations).map(k=>([k.name, k]))).values()];
console.log(result);
Or if you do not want to use Map then use filter method:
var data=[{ id: 'id-1', uniqueName: 'operation-level-1', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-2', label: 'operation-2-label' } ] }, { id: 'id-2', uniqueName: 'operation-level-2', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-3', label: 'operation-3-label' } ] }];
var result = data.flatMap(({operations})=>operations).filter((val,i,self)=>self.findIndex(j=>j.name==val.name && j.label==val.label)==i);
console.log(result);
Use _.flatMap() to get a flattened array of operations, and then use _.uniqBy() to get only items with unique name:
const data=[{ id: 'id-1', uniqueName: 'operation-level-1', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-2', label: 'operation-2-label' } ] }, { id: 'id-2', uniqueName: 'operation-level-2', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-3', label: 'operation-3-label' } ] }];
const result = _.uniqBy(
_.flatMap(data, 'operations'),
'name'
);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
With lodash/fp you can generate a function using _.flow() that flattens operations, and then get the unique values by name:
const fn = _.flow(
_.flatMap('operations'),
_.uniqBy('name')
)
const data=[{ id: 'id-1', uniqueName: 'operation-level-1', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-2', label: 'operation-2-label' } ] }, { id: 'id-2', uniqueName: 'operation-level-2', operations: [ { name: 'operaion-1', label: 'operation-1-label' }, { name: 'operaion-3', label: 'operation-3-label' } ] }];
const result = fn(data);
console.log(result);
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
There is no need for loadash, a simple Map object, will allow you to suppress the duplicates.
const arr = [{
id: 'id-1',
uniqueName: 'operation-level-1',
operations: [{
name: 'operaion-1',
label: 'operation-1-label'
},
{
name: 'operaion-2',
label: 'operation-2-label'
}
]
},
{
id: 'id-2',
uniqueName: 'operation-level-2',
operations: [{
name: 'operaion-1',
label: 'operation-1-label'
},
{
name: 'operaion-3',
label: 'operation-3-label'
}
]
}
];
// Go thought the arr and add all the name/label into the map
// Then use Array.from to reformate the Map into the wanted format
const uniqueArr = Array.from(arr.reduce((tmp, {
operations,
}) => {
operations.forEach(({
name,
label,
}) => {
tmp.set(name, label);
});
return tmp;
}, new Map())).map(([name, label]) => ({
name,
label,
}));
console.log(uniqueArr);

How to flat tree, auto generation parent?

I want to keep the parent-child relationship of the tree node.
I have a JSON tree, like this
{
id: null,
children: [
{
id: 1,
children: [
{
id: 11,
children: [
{
id: 111
children: []
}
]
},
{
id: '12',
children: []
},
]
},
{
id: '2',
children: [
{
id: '21',
children: []
},
{
id: '22',
children: [
{
id: '221',
children: []
}
]
},
]
},
]
}
I want flat the tree, like this
[
{ id: 1, parent: null,},
{ id: 11, parent: 1, },
{ id: 111, parent: 11, },
{ id: 2, parent: null, },
{ id: 21, parent: 2, },
...
]
parent automatic generated
Is there any good way?
You could use flatMap method and create recursive function that will return 1D array as a result.
const data = {"id":null,"children":[{"id":1,"children":[{"id":11,"children":[{"id":111,"children":[]}]},{"id":"12","children":[]}]},{"id":"2","children":[{"id":"21","children":[]},{"id":"22","children":[{"id":"221","children":[]}]}]}]}
const flatten = (data, parent = null) =>
data.flatMap(({ id, children }) => ([
{ id, parent },
...flatten(children, id)
]))
const result = flatten(data.children);
console.log(result)
You could get the object and return an array of the flat children.
const
getFlat = ({ id, children = [] }) =>
children.flatMap(o => [{ id: o.id, parent: id }, ...getFlat(o)]);
var data = { id: null, children: [{ id: 1, children: [{ id: 11, children: [{ id: 111, children: [] }] }, { id: '12', children: [] }] }, { id: '2', children: [{ id: '21', children: [] }, { id: '22', children: [{ id: '221', children: [] }] }] }] },
flat = getFlat(data);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If compatbility with Internet Explorer is important, then the following approach which avoids the need for Array#flatMap might suit:
const input={id:null,children:[{id:'1',children:[{id:'11',children:[{id:'111',children:[]}]},{id:'12',children:[]}]},{id:'2',children:[{id:'21',children:[]},{id:'22',children:[{id:'221',children:[]}]}]}]};
const result = [];
// Define recursive function to walk through the data tree
// and produce a flat array of required data
const recurse = (item, parent) => {
// Only add item to result if it has valid id
if (item.id) {
result.push({
id: item.id,
parent: parent ? parent.id : null
});
}
// Iterate the children of this item, traversing and
// processing them in the same way
item.children.forEach(child => recurse(child, item))
}
recurse(input);
console.log(result);
You can use a simple recursion to achieve this
let data = {
id: 1,
name: 'a1',
children: [{
id: 2,
name: 'a2',
children: [{
id: 3,
name: 'b1',
children: []
},
{
id: 4,
name: 'b2',
children: []
}
]
}]
}
function flatten(data){
output = [];
return (function indentHandler(data, level, parent){
output.push({id: data['id'], parent: parent})
if (data['children'].length === 0){
return output;
}
level += 1;
data['children'].forEach(function(child){
return indentHandler(child, level, data.name);
});
return output;
})(data, 0, null);
}
flatten(data);
use recursion
var tree = {
id: null,
children: [{
id: 1,
children: [{
id: 11,
children: [{
id: 111,
children: []
}]
},
{
id: '12',
children: []
},
]
},
{
id: '2',
children: [{
id: '21',
children: []
},
{
id: '22',
children: [{
id: '221',
children: []
}]
},
]
}
]
};
var flat = [];
function flatArray(arr, parentId) {
arr.forEach(function(el) {
flat.push({
id: el.id,
parent: parentId || null
});
if (el.children)
flatArray(el.children, el.id);
});
}
flatArray(tree.children, 0)
console.log(flat);

How to fetch data from complex array objects structures?

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

Categories

Resources