Generate permutations of Javascript object keys/values - javascript

I need to create a nested hierarchy of options. The options are keys on an array with several sub-options as nested objects.
I need to generate a nested hierarchy from this object.
Starting with an object like this:
const starterObject = {
id1: {
1: { value: "A" },
2: { value: "B" },
3: { value: "C" },
},
id2: {
1: { value: 10 },
2: { value: 20 },
},
};
I need to end up with an object of permutations like this:
const permutations2 = {
1: [{ value: "A" }, { value: 10 }],
2: [{ value: "A" }, { value: 20 }],
3: [{ value: "B" }, { value: 10 }],
4: [{ value: "B" }, { value: 20 }],
5: [{ value: "C" }, { value: 10 }],
6: [{ value: "C" }, { value: 20 }],
};
I tried something like this:
const starterObject = {
id1: {
1: { value: "A" },
2: { value: "B" },
3: { value: "C" },
},
id2: {
1: { value: 10 },
2: { value: 20 },
},
};
const permutationMatrix = [];
Object.keys(starterObject["id1"]).forEach(key2 =>
Object.keys(starterObject["id2"]).forEach(key1 =>
permutationMatrix.push([
starterObject["id1"][key2],
starterObject["id2"][key1],
])
)
);
console.log(permutationMatrix)
But the problem is that the keys are hardcoded. The actual object will have 1-5 keys (id1 - id5) and any number of nested objects.
I think this will require recursion, but I'm not sure how to proceed from here.

Reduce, entries, and values can help and there is no need for recursion.
const starterObject = {
id1: {
1: { value: "A" },
2: { value: "B" },
3: { value: "C" },
},
id2: {
1: { value: 10 },
2: { value: 20 },
},
id3: {
1: { value: 100 }
}
};
var entries = Object.entries(starterObject)
var out = entries.reduce((arr, group) => {
const itms = Object.values(group)[1]
const vals = Object.values(itms)
// if first time, set up the initial arrays with first set of data
if (!arr.length) {
return vals.map(v => [v])
}
// after first one, we will just loop over the arrays
// and start adding the next set of data to each one
return arr.reduce((updated, curr) => {
vals.forEach(val => {
// make copy so we are not adding data to the reference
const copy = curr.slice()
copy.push(val)
updated.push(copy)
})
return updated
}, [])
}, [])
console.log(JSON.stringify(out))

You could use recursion for this. Your input/output format is somewhat peculiar with its array-like objects, with keys starting at 1. So for that I would suggest a separate wrapper function, which only takes care of such format conversions.
I had a go at a functional approach, creating separate functions for each of the callbacks needed in the process:
const clone = o => ({...o});
const prefixer = item => arr => [item, ...arr].map(clone);
const merger = arr => item => arr.map(prefixer(item));
const extender = group => res => group.flatMap(merger(res));
// The recursive function based on standard array format
const cross = (group, ...rest) => group ? extender(group)(cross(...rest)) : [[]];
// For dealing with the unconventional input/output format:
const baseOne = (x, i) => [i+1, x];
const generatePermutations = obj =>
Object.fromEntries(cross(...Object.values(obj).map(Object.values)).map(baseOne));
// Sample data & call
const starterObject = {
id1: {
1: { value: "A" },
2: { value: "B" },
3: { value: "C" },
},
id2: {
1: { value: 10 },
2: { value: 20 },
},
};
const permutations = generatePermutations(starterObject);
console.log(permutations);

Related

How to transform an object into an array of objects by its keys?

I've got a data source:
const data = {
A: [{
value: 1
}, {
value: 2
}, {
value: 38
}],
B: [{
value: 46
}, {
value: 23
}, {
value: 32
}],
C: [{
value: 2345
}, {
value: 56
}, {
value: 3
}]
}
I need to transform this object in an array of objects like below:
[{
A: 1,
B: 46,
C: 2345
}, {
A: 2,
B: 23,
C: 56
}, {
A: 38,
B: 32,
C: 3
}]
I made some attempts but still not there:
a)
const result = Object.keys(data).map(key => data[key])
b)
const b = Object.keys(data).reduce((acc, curr, i) => {
const values = data[curr].map(el => el.value)
acc[curr] = values[i]
return acc
}, [])
EDIT:
All arrays (A, B, C) should have the same length. In case it doesn't, value should be "-" for the missing ones
eg:
[{
A: 1,
B: 46,
C: 2345
}, {
A: 2,
B: 23,
C: 56
}, {
A: 38,
B: 32,
C: -
}]
Assuming the number of objects in your value arrays is the same (or less than) the number of properties in your data object, you could map your data keys to new objects that you can create using Object.fromEntries(). You can pass an array of mapped values to this fromEntries call, which goes through all your keys a, b, c and uses the current index from the outer loop to determine which object to grab its value from:
const data = { A: [{ value: 1 }, { value: 2 }, { value: 38 }], B: [{ value: 46 }, { value: 23 }, { value: 32 }], C: [{ value: 2345 }, { value: 56 }, { value: 3 }] };
const res = Object.keys(data).map((_, i, arr) =>
Object.fromEntries(arr.map(key => [key, data[key][i]?.value ?? "-"]))
);
console.log(res);
If you need a more robust approach, I suggest you go with a solution such as T.J. Crowder's that can handle the case where there are more objects in your arrays than there are properties in your object.
I'm going to assume that the fact there are three properties (A, B, and C) and the fact there are three values for each of them is a coincidence and that we can't rely on that.
If so, see comments:
// Get the keys
const keys = Object.keys(data);
// A blank object to use as a template for a new array entry w/blank values
const blankEntry = Object.fromEntries(keys.map(key => [key, "-"]));
// Create the result array; since we don't know how long it'll need to
// be without reading through the array, just start with a blank one)
const result = [];
// Loop through the properties
for (const key of keys) {
// Loop through this property's values
const values = data[key];
for (let index = 0; index < values.length; ++index) {
// Get the object at this index, creating it if not there
let entry = result[index];
if (!entry) {
// Make a shallow copy of the template to create the entry
result[index] = entry = {...blankEntry};
}
// Set this key's value
entry[key] = values[index].value;
}
}
Live Example (I've added a fourth entry to A to show that the three and three thing isn't important to the solution, and to show the "-" thing working):
const data = {
A: [{
value: 1
}, {
value: 2
}, {
value: 38
}, {
value: 42
}],
B: [{
value: 46
}, {
value: 23
}, {
value: 32
}],
C: [{
value: 2345
}, {
value: 56
}, {
value: 3
}]
};
const keys = Object.keys(data);
const blankEntry = Object.fromEntries(keys.map(key => [key, "-"]));
const result = [];
for (const key of keys) {
const values = data[key];
for (let index = 0; index < values.length; ++index) {
let entry = result[index];
if (!entry) {
result[index] = entry = {...blankEntry};
}
entry[key] = values[index].value;
}
}
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
You could get the max length first and then map the values.
const
data = { A: [{ value: 1 }, { value: 2 }, { value: 38 }, { value: 99 }], B: [{ value: 46 }, { value: 23 }, { value: 32 }], C: [{ value: 2345 }, { value: 56 }, { value: 3 }] },
entries = Object.entries(data),
length = entries.reduce((r, [, { length }]) => Math.max(r, length), 0),
result = entries.reduce(
(r, [k, a]) => r.map((o, i) => ({ ...o, ...(i in a && { [k]: a[i].value }) })),
Array.from(
{ length },
() => Object.fromEntries(entries.map(([k]) => [k, '-']))
)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can create an empty object with - and assign value after with reduce() and forEach().
const data = { A: [ { value: 1, }, { value: 2, }, { value: 38, }, ], B: [ { value: 46, }, { value: 23, }, { value: 32, }, ], C: [ { value: 2345, }, { value: 56, }, { value: 3, }, ], };
const keys = Object.keys(data);
const emptyObj = keys.map(_ =>
keys.reduce((o, key) => ({ ...o, [key]: "-" }), {})
);
const o = keys.reduce((a, b) => {
data[b].forEach((el, i) => {
a[i][b] = el.value;
});
return a;
}, emptyObj);
console.log(o);
I think I'd try with something like:
// input
const data = {
A: [{ value: 1 }, { value: 2 }, { value: 38 }, { value: 57 }],
B: [{ value: 46 }, { value: 23 }, { value: 32 }, { value: 42 }],
C: [{ value: 2345 }, { value: 56 }, { value: 3 }],
D: [{ value: 345 }, { value: 684 }]
};
// I am using an IIFE to initialize `output` in order not to pollute the global scope
// with variables that are not re-used anywhere else but obviously it is not strictly necessary.
const output = (() => {
const keys = Object.keys(data);
const maxItemsNr = Math.max(...Object.values(data).map(item => item.length));
const newArr = [];
for (let i = 0; i < maxItemsNr; i++) {
const item = keys.reduce((outObj, key) => {
outObj[key] = data[key][i] ? data[key][i].value : '-';
return outObj;
}, {});
newArr.push(item);
}
return newArr
})();
// test
console.log(output);
And this is my take on it:
const data = { A: [{value: 1}, {value: 2}],
B: [{value: 46}, {value: 23}, {value: 32}, {value: 38}, {value: 42}],
C: [{value: 2345}, {value: 56}, {value: 3}] };
const ntrs = Object.entries(data),
blnk = ntrs.reduce((a,[key])=>(a[key]="-",a),{}),
rslt = [];
ntrs.forEach(([k,arr])=> arr.forEach(({value},i)=>(rslt[i]=rslt[i]||{...blnk})[k]=value) )
console.log(rslt);
.as-console-wrapper {
max-height: 100% !important;
}
My solution. But I think Nick Parson's solution is better.
const data = {A: [{value: 1}, {value: 2}, {value: 38}],B: [{value: 46}, {value: 23}, {value: 32}],C: [{value: 2345}, {value: 56}, {value: 3}]}
function transform(object) {
const res = [];
Object.keys(object).forEach((key)=> {
object[key].forEach(({value},i) => {
if(res[i]) {
res[i][key] = value
} else {
res[i] = {
[key]: value
}
}
})
})
return res
}
console.log(transform(data))

How to change the current array format to just an array of objects format?

How can I change the following data structure that is coming from the api into the desired format that is defined below?
inspectionViewAllRs = [
{
INSPN_XFER_SEQ_NR: {
value: '5'
},
FNRL_FLG: {
label: 'No',
value: 'N'
},
HAS_PAST_INSPN: {
value: '3'
},
MTG_CO_PHN_AREA_CD: {
value: ''
},
DECLARATION_CD: {
label: 'US Citizen/Non-Citizen National',
value: 'CZ'
},
....
....
....
},
{ ... }
{ ... }
{ ... }
]
How can I just convert it into this an array of objects format?
inspectionViewAllRs = [
{ label: "", value: '5' },
{ label: "No", value: "N" },
{ label: "", value: "3" },
{ label: "", value: "" },
{ label: "US Citizen/Non-Citizen National", value: "CZ" },
....
....
....
]
I tried to do the following but this doesn't help much:
if(!_.isEmpty(this.state.inspectionViewAllRsData)) {
const result = this.state.inspectionViewAllRsData.map((data, i) => {
const items = Object.values(data);
const newItem = {}
newItem.label = items[i].label;
newItem.value = items[i].value;
return newItem
})
console.log("result ", result)
// this is what I see printed out in console log
// result (5) [{…}, {…}, {…}, {…}, {…}]
0: {label: undefined, value: "5"}
1: {label: "No", value: "N"}
2: {label: undefined, value: "3"}
3: {label: undefined, value: ""}
4: {label: "", value: ""}
length: 5
}
Why am I not getting return all the rest of the data?
Using Array#flatMap and Object#values to get one list of the objects values, and then Array#map to get the same attributes in all:
const inspectionViewAllRs = [
{
INSPN_XFER_SEQ_NR: { value: '5' },
FNRL_FLG: { label: 'No', value: 'N' },
HAS_PAST_INSPN: { value: '3' },
MTG_CO_PHN_AREA_CD: { value: '' },
DECLARATION_CD: { label: 'US Citizen/Non-Citizen National', value: 'CZ' },
},
{
DECLARATION_CD: { label: 'US Citizen/Non-Citizen National', value: 'CZ' },
}
];
const res = inspectionViewAllRs
.flatMap(Object.values) // get one list of objects
.map(({ label = '', value = '' }) => ({ label, value })); // return list of label/value objects
console.log(res);
You will need to use .flatMap and map on the items .values.
You API repsonse looks like:
[
{ unneeded_keys: Needed_Values}, //objectOfData
{ unneeded_keys: Needed_Values},
{ unneeded_keys: Needed_Values},
]
Your state.inspectionViewAllRsData array contains these objects. The actual data you need is inside Needed_Values.
So, you need to do:
// Note: flatMap here \/\/\/
const result = this.state.inspectionViewAllRsData.flatMap((objectOfData) => {
const items = Object.values(data);
return items.map((item, i) => {
return {
label: item.label ?? '', // handle case when label is undefined
value: item.value,
}
}
})
The reason you need to use flatMap is because each obj in your API response, will return multiple elements as per your transformed data. So, flatMap will automatically flatten the array of array of objects to array of objects.

JavaScript Equivalent to C# LINQ or another way to fetch data [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
I have the JS array something like that
var arr = [{
TimeEdit: "2020-10-29T10:45:00.21Z",
Date: "2020-10-29T00:00:00",
Parameters: [ { Id: 1, Value: 1}, { Id: 2, Value: 348 }]
},{
TimeEdit: "2020-10-29T10:43:52.22Z",
Date: "2020-10-29T00:00:00",
Parameters: [ { Id: 1, Value: 12}, { Id: 2, Value: 348 }]
},{
TimeEdit: "2020-10-30T10:47:12.33Z",
Date: "2020-10-30T00:00:00",
Parameters: [ { Id: 1, Value: 3}, { Id: 2, Value: 2 }]
}];
and I want to select from this array data with a unique "Date" sorted by "TimeEdit" so that at the output I can get this array
[{
TimeEdit: "2020-10-29T10:45:00.21Z",
Date: "2020-10-29T00:00:00",
Parameters: [ { Id: 1, Value: 1}, { Id: 2, Value: 348 }]
},{
TimeEdit: "2020-10-30T10:47:12.33Z",
Date: "2020-10-30T00:00:00",
Parameters: [ { Id: 1, Value: 3}, { Id: 2, Value: 2 }]
}];
In C# code I would do something like that:
var dates = arr.Select(r => r.Date).Distinct().ToList();
foreach (var date in dates)
{
WorkLog log = arr.Where(r => r.Datetime == date).OrderByDescending(r => r.TimeEdit).FirstOrDefault();
//to do some stuff here
}
So what is the best way to do so in JS?
JavaScript has a number of built-in functions that help you in working with arrays in a linq-ish way. Functions like map (equivalent of Select) and filter (equivalent of Where) come to mind. Check out MDN for docs.
Even though these can be very useful, it can be useful to use another library for working with arrays or objects. From my experience lodash is very handy for manipulating arrays. It has a chain method that you can use to express operations in a very linq-ish way.
const dates = _.chain(arr).map(r => r.Date).uniq().value();
dates.forEach(date => {
const log = _.chain(arr)
.filter(r => r.Datetime === date)
.sortBy(r => r.TimeEdit)
.reverse()
.head();
//to do some stuff here
});
You could mimic the behaviour with some own function for grouping, ordering, sorting and getting the first item of each group. Fo binding all together, you need a pipe function as well.
const
pipe = (...functions) => input => functions.reduce((acc, fn) => fn(acc), input),
groupBy = key => array => array.reduce((r, o) => {
var fn = typeof key === 'function' ? key : o => o[key],
temp = r.find(([p]) => fn(o) === fn(p));
if (temp) temp.push(o);
else r.push([o]);
return r;
}, []),
order = (...keys) => array => array.sort((a, b) => {
var result;
keys.some(k => result = a[k] > b[k] || -(a[k] < b[k]));
return result
}),
select = fn => array => array.map(fn),
first = array => array[0],
data = [{ TimeEdit: "2020-10-29T10:45:00.21Z", Date: "2020-10-29T00:00:00", Parameters: [{ Id: 1, Value: 1 }, { Id: 2, Value: 348 }] }, { TimeEdit: "2020-10-29T10:43:52.22Z", Date: "2020-10-29T00:00:00", Parameters: [{ Id: 1, Value: 12 }, { Id: 2, Value: 348 }] }, { TimeEdit: "2020-10-30T10:47:12.33Z", Date: "2020-10-30T00:00:00", Parameters: [{ Id: 1, Value: 3 }, { Id: 2, Value: 2 }] }],
result = pipe(
groupBy('Date'),
select(
pipe(
order('TimeEdit'),
first
)
)
)(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Reduce and sort object array in one step

I'm trying to get two values (label and data) from my source data array, rename the label value and sort the label and data array in the result object.
If this is my source data...
const sourceData = [
{ _id: 'any', count: 12 },
{ _id: 'thing', count: 34 },
{ _id: 'value', count: 56 }
];
...the result should be:
{ label: ['car', 'plane', 'ship'], data: [12, 34, 56] }
So any should become car, thing should become plane and value should become ship.
But I also want to change the order of the elements in the result arrays using the label values, which should also order the data values.
Let's assume this result is expected:
{ label: ['ship', 'car', 'plane'], data: [56, 12, 34] }
With the following solution there is the need of two variables (maps and order). I thing it would be better to use only one kind of map, which should set the new label values and also the order. Maybe with an array?!
Right now only the label values get ordered, but data values should be ordered in the same way...
const maps = { any: 'car', thing: 'plane', value: 'ship' }; // 1. Rename label values
const result = sourceData.reduce((a, c) => {
a.label = a.label || [];
a.data = a.data || [];
a.label.push(maps[c._id]);
a.data.push(c.count);
return a;
}, {});
result.label.sort((a, b) => {
const order = {'ship': 1, 'car': 2, plane: 3}; // 2. Set new order
return order[a] - order[b];
})
You could move the information into a single object.
const
data = [{ _id: 'any', count: 12 }, { _id: 'thing', count: 34 }, { _id: 'value', count: 56 }],
target = { any: { label: 'car', index: 1 }, thing: { label: 'plane', index: 2 }, value: { label: 'ship', index: 0 } },
result = data.reduce((r, { _id, count }) => {
r.label[target[_id].index] = target[_id].label;
r.data[target[_id].index] = count;
return r;
}, { label: [], data: [] })
console.log(result);
Instead of separating the data into label and data and then sorting them together, you can first sort the data and then transform.
const sourceData = [
{ _id: 'any', count: 12 },
{ _id: 'thing', count: 34 },
{ _id: 'value', count: 56 }
];
const maps = { any: 'car', thing: 'plane', value: 'ship' };
// Rename label values.
let result = sourceData.map(item => ({
...item,
_id: maps[item._id]
}));
// Sort the data.
result.sort((a, b) => {
const order = {'ship': 1, 'car': 2, plane: 3};
return order[a._id] - order[b._id];
})
// Transform the result.
result = result.reduce((a, c) => {
a.label = a.label || [];
a.data = a.data || [];
a.label.push(c._id);
a.data.push(c.count);
return a;
}, {});
console.log(result);

Merge JavaScript objects in array with same key

What is the best way to merge array contents from JavaScript objects sharing a key in common?
How can array in the example below be reorganized into output? Here, all value keys (whether an array or not) are merged into all objects sharing the same name key.
var array = [
{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}
];
var output = [
{
name: "foo1",
value: [
"val1",
"val2",
"val3"
]
}, {
name: "foo2",
value: [
"val4"
]
}
];
Here is one option:-
var array = [{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: ["val2", "val3"]
}, {
name: "foo2",
value: "val4"
}];
var output = [];
array.forEach(function(item) {
var existing = output.filter(function(v, i) {
return v.name == item.name;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
output[existingIndex].value = output[existingIndex].value.concat(item.value);
} else {
if (typeof item.value == 'string')
item.value = [item.value];
output.push(item);
}
});
console.dir(output);
Here is another way of achieving that goal:
var array = [{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}];
var output = array.reduce(function(o, cur) {
// Get the index of the key-value pair.
var occurs = o.reduce(function(n, item, i) {
return (item.name === cur.name) ? i : n;
}, -1);
// If the name is found,
if (occurs >= 0) {
// append the current value to its list of values.
o[occurs].value = o[occurs].value.concat(cur.value);
// Otherwise,
} else {
// add the current item to o (but make sure the value is an array).
var obj = {
name: cur.name,
value: [cur.value]
};
o = o.concat([obj]);
}
return o;
}, []);
console.log(output);
2021 version
Using reduce to aggregate data.
Using logical nullish assignment only assigns if acc[name] is nullish (null or undefined).
Using Array.isArray to determines whether the passed value is an Array.
var arrays = [{ name: "foo1",value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2",value: "val4"}];
const result = arrays.reduce((acc, {name, value}) => {
acc[name] ??= {name: name, value: []};
if(Array.isArray(value)) // if it's array type then concat
acc[name].value = acc[name].value.concat(value);
else
acc[name].value.push(value);
return acc;
}, {});
console.log(Object.values(result));
Using lodash
var array = [{name:"foo1",value:"val1"},{name:"foo1",value:["val2","val3"]},{name:"foo2",value:"val4"}];
function mergeNames (arr) {
return _.chain(arr).groupBy('name').mapValues(function (v) {
return _.chain(v).pluck('value').flattenDeep();
}).value();
}
console.log(mergeNames(array));
Here is a version using an ES6 Map:
const arrays = [{ name: "foo1",value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2",value: "val4"}];
const map = new Map(arrays.map(({name, value}) => [name, { name, value: [] }]));
for (let {name, value} of arrays) map.get(name).value.push(...[value].flat());
console.log([...map.values()]);
Use lodash "uniqWith". As shown below
let _ = require("lodash");
var array = [
{ name: "foo1", value: "1" },
{ name: "foo1", value: "2" },
{ name: "foo2", value: "3" },
{ name: "foo1", value: "4" }
];
let merged = _.uniqWith(array, (pre, cur) => {
if (pre.name == cur.name) {
cur.value = cur.value + "," + pre.value;
return true;
}
return false;
});
console.log(merged);
// output: [{ name: "foo1", value: "1,2,4" }, { name: "foo2", value: "3" }];
Using reduce:
var mergedObj = array.reduce((acc, obj) => {
if (acc[obj.name]) {
acc[obj.name].value = acc[obj.name].value.isArray ?
acc[obj.name].value.concat(obj.value) :
[acc[obj.name].value].concat(obj.value);
} else {
acc[obj.name] = obj;
}
return acc;
}, {});
let output = [];
for (let prop in mergedObj) {
output.push(mergedObj[prop])
}
It's been a while since this question was asked, but I thought I'd chime in as well. For functions like this that execute a basic function you'll want to use over and over, I prefer to avoid longer-written functions and loops if I can help it and develop the function as a one-liner using shallow Array.prototype functions like .map() and some other ES6+ goodies like Object.entries() and Object.fromEntries(). Combining all these, we can execute a function like this relatively easily.
First, I take in however many objects you pass to the function as a rest parameter and prepend that with an empty object we'll use to collect all the keys and values.
[{}, ...objs]
Next, I use the .map() Array prototype function paired with Object.entries() to loop through all the entries of each object, and any sub-array elements each contains and then either set the empty object's key to that value if it has not yet been declared, or I push the new values to the object key if it has been declared.
[{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]
Finally, to replace any single-element-arrays with their contained value, I run another .map() function on the result array using both Object.entries() and Object.fromEntries(), similar to how we did before.
let getMergedObjs = (...objs) => Object.fromEntries(Object.entries([{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]).map(e => e.map((f,i) => i ? (f.length > 1 ? f : f[0]) : f)));
This will leave you with the final merged object, exactly as you prescribed it.
let a = {
a: [1,9],
b: 1,
c: 1
}
let b = {
a: 2,
b: 2
}
let c = {
b: 3,
c: 3,
d: 5
}
let getMergedObjs = (...objs) => Object.fromEntries(Object.entries([{},...objs].map((e,i,a) => i ? Object.entries(e).map(f => (a[0][f[0]] ? a[0][f[0]].push(...([f[1]].flat())) : (a[0][f[0]] = [f[1]].flat()))) : e)[0]).map(e => e.map((f,i) => i ? (f.length > 1 ? f : f[0]) : f)));
getMergedObjs(a,b,c); // { a: [ 1, 9, 2 ], b: [ 1, 2, 3 ], c: [ 1, 3 ], d: 5 }
Try this:
var array = [{name:"foo1",value:"val1"},{name:"foo1",value:["val2","val3"]},{name:"foo2",value:"val4"},{name:"foo2",value:"val5"}];
for(var j=0;j<array.length;j++){
var current = array[j];
for(var i=j+1;i<array.length;i++){
if(current.name = array[i].name){
if(!isArray(current.value))
current.value = [ current.value ];
if(isArray(array[i].value))
for(var v=0;v<array[i].value.length;v++)
current.value.push(array[i].value[v]);
else
current.value.push(array[i].value);
array.splice(i,1);
i++;
}
}
}
function isArray(myArray) {
return myArray.constructor.toString().indexOf("Array") > -1;
}
document.write(JSON.stringify(array));
This work too !
var array = [
{
name: "foo1",
value: "val1",
},
{
name: "foo1",
value: ["val2", "val3"],
},
{
name: "foo2",
value: "val4",
},
];
let arr2 = [];
array.forEach((element) => { // remove duplicate name
let match = arr2.find((r) => r.name == element.name);
if (match) {
} else {
arr2.push({ name: element.name, value: [] });
}
});
arr2.map((item) => {
array.map((e) => {
if (e.name == item.name) {
if (typeof e.value == "object") { //lets map if value is an object
e.value.map((z) => {
item.value.push(z);
});
} else {
item.value.push(e.value);
}
}
});
});
console.log(arr2);
const exampleObj = [{
year: 2016,
abd: 123
}, {
year: 2016,
abdc: 123
}, {
year: 2017,
abdcxc: 123
}, {
year: 2017,
abdcxcx: 123
}];
const listOfYears = [];
const finalObj = [];
exampleObj.map(sample => {    
listOfYears.push(sample.year);
});
const uniqueList = [...new Set(listOfYears)];
uniqueList.map(list => {   
finalObj.push({
year: list
});
});
exampleObj.map(sample => {    
const sampleYear = sample.year;  
finalObj.map((obj, index) => {     
if (obj.year === sampleYear) {        
finalObj[index] = Object.assign(sample, obj);       
}  
}); 
});
The final object be [{"year":2016,"abdc":123,"abd":123},{"year":2017,"abdcxcx":123,"abdcxc":123}]
const array = [{ name: "foo1", value: "val1" }, {name: "foo1", value: ["val2", "val3"] }, {name: "foo2", value: "val4"}];
const start = array.reduce((object, {name}) => ({...object, [name]: []}), {});
const result = array.reduce((object, {name, value}) => ({...object, [name]: [object[name], [value]].flat(2)}), start);
const output = Object.entries(result).map(([name, value]) => ({name: name, value: value}));
console.log(output);
try this :
var array = [
{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}
];
var output = [
{
name: "foo1",
value: [
"val1",
"val2",
"val3"
]
}, {
name: "foo2",
value: [
"val4"
]
}
];
bb = Object.assign( {}, array, output );
console.log(bb) ;
A much more easier approach is this 2022:
var array = [
{
name: "foo1",
value: "val1"
}, {
name: "foo1",
value: [
"val2",
"val3"
]
}, {
name: "foo2",
value: "val4"
}
];
var output = [
{
name: "foo1",
value: [
"val1",
"val2",
"val3"
]
},
{
name: "foo2",
value: [
"val4"
]
}
];
function mergeBasedOnKey(list){
let c = Object.values(list.reduce((a, b) => {
a[b.name] = a[b.name] || {name: b.name, value: []}
if(typeof(b['value']) == "string"){
a[b.name].value.push(b['value'])
}
else{
a[b.name].value = [...a[b.name].value, ...b.value]
}
return a
}, {}))
return c
}
let ans = mergeBasedOnKey(array)
console.log(ans)
I was looking for a quick, almost "one-liner" answer in this thread, provided that this is a trivial but common exercise.
I couldn't find any for my like. The other answers are fine but I am not much into boilerplate.
So, let me add one, then:
o = array.reduce((m,{name:n,value:v})=>({...m,[n]:[...m[n]||[],v].flat(1)}),{})
output = Object.entries(o).map(([n,v])=>({name:n,value:v}))
var array = [
{ name: "foo1", value: "val1"},
{ name: "foo1", value: ["val2","val3"] },
{ name: "foo2", value: "val4" }
]
o=array.reduce((m,{name:n,value:v})=>({...m,[n]:[...m[n]||[],v].flat(1)}),{})
output=Object.entries(o).map(([n,v])=>({name:n,value:v}))
console.log(output)

Categories

Resources