Related
I'm stuck at transforming a data structure:
let d = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 }
]
];
d = d.reduce((accum, o) => {
}, [])
How can I produce this?
[{name: 'no 1', score: [7000, 8500]}, {name: 'no 2', score: [10000, 6500]}]
Here is one way to do it with simple reduce,
const result = d.flat().reduce((acc: {name: string, score: number[]}[], curr) => {
const { no, score } = curr;
let item = acc.find(a => a.name === `no ${no}`);
if (!item) {
item = { name: `no ${no}`, score: []};
acc.push(item);
}
item.score.push(score);
return acc;
}, []);
console.log(result)
let d = [{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[ { no: 1, score: 8500 },
{ no: 2, score: 6500 }]]
const result=d.flat().reduce((acc,curr)=>{
if(acc[curr.no]){
acc[curr.no].score.push(curr.score)
} else {
const keys=Object.keys(curr)
acc[curr.no]={ name: keys[0]+ ' '+curr.no, score:[curr.score]}
}
return acc;
},{})
console.log(Object.values(result))
Array.prototype.flat() call before the actual grouping, then use reduce function create the result.
let d = [{
no: 1,
score: 7000
},
{
no: 2,
score: 10000
},
[{
no: 1,
score: 8500
},
{
no: 2,
score: 6500
}
]
]
const result = d.flat().reduce((result, element) => {
const key = element.no;
if (!result[key]) {
result[key] = {
name: `no ${key}`,
score: []
}
}
result[key].score.push(element.score);
return result;
}, {})
console.log(Object.values(result))
Here's a sleek functional solution for typescript, since you included the tag-
const arr = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 },
],
];
const result = Object.entries(
arr.flat().reduce((accum: { [key: number]: number[] }, el: { no: number; score: number }) => {
accum[el.no] = (accum[el.no] ?? []).concat(el.score);
return accum;
}, {})
).map(([num, scores]) => ({ no: Number(num), scores: scores }));
console.log(result);
Result-
[
{ no: 1, scores: [ 7000, 8500 ] },
{ no: 2, scores: [ 10000, 6500 ] }
]
This flattens the inner arrays first using Array.prototype.flat. Then it uses reduce to construct an object that has the no values as keys and score values as an array of values.
In the end, the reduce results in { 1: [7000, 8500], 2: [10000, 6500] } - turn that into entries using Object.entries to get [['1', [7000, 8500]], ['2', [10000, 6500]]]
Finally, map over the entries to turn the ['1', [7000, 8500]] format into { no: 1, scores: [ 7000, 8500 ] } format and you're done!
You could take a dynamic approach which groups by the given key and takes all other properties for a new array.
const
groupBy = key => (r, value) => {
if (Array.isArray(value)) return value.reduce(group, r);
const { [key]: _, ...o } = value;
Object.entries(o).forEach(([k, v]) => ((r[_] ??= { [key]: _ })[k] ??= []).push(v));
return r;
}
data = [{ no: 1, score: 7000 }, { no: 2, score: 10000 }, [{ no: 1, score: 8500 }, { no: 2, score: 6500 }]],
group = groupBy('no'),
result = Object.values(data.reduce(group, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.prototype.flat()
or if you have any array like,
let d = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 }
]
];
and then use d.flat()
Try this:
let d = [
{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[
{ no: 1, score: 8500 },
{ no: 2, score: 6500 }
]
]
const reducer = (arr, start = []) => arr.reduce((acc, next) => {
if (Array.isArray(next)) return reducer(next, acc);
for (const value of acc) {
if (value.name === `no ${next.no}`) {
value.score = [...value.score, next.score];
return acc;
}
}
return [...acc, {
name: `no ${next.no}`,
score: [next.score]
}];
}, start);
console.log(reducer(d));
In your case, you not only need to flatten the list, but also group by no property.
For flatting, you can use Array.prototype.flat(). It's quite a new feature, so if you don't use polyfills, you probably can't use it. So you can check for alternative implementations.
For grouping, you can reduce to the object where the key is no property. Note, that if multiple no properties exist, you need to save an array of all score values.
Example:
const d = [{ no: 1, score: 7000 },
{ no: 2, score: 10000 },
[ { no: 1, score: 8500 },
{ no: 2, score: 6500 }]]
const grouped = d.flat().reduce((prev, cur) => {
if (cur.no in prev) {
prev[cur.no].score.push(cur.score)
} else {
prev[cur.no] = {
name: 'no ' + cur.no,
score: [cur.score]
}
}
return prev
}, {})
console.log(Object.values(grouped))
In the example we use modifications. It is possible to do it without modifications - return a new copy during each reduction iteration. However, depending on array size, there can be performance issues. Also, it's safe to do modifications, because we create a new object in this case.
I'm doing a complex filter, for which I have an initial list of objects with unique ids.
And a map with N properties with a list of corresponding object copies for each prop; and three states for each prop: idle: 0, show: 1, hide: 2.
For now I managed to do this with lodash's differenceBy and intersectionBy. My filter function takes in an array of objects and mutates the array, by checking and filtering the array with every map prop.
But concerning efficiency and growing number of complexity, should this kind of problem be solved differently?
For example:
If this filter is applied to a big array of hex colors (length 100, 1000 or more)
colors [1, 2, 3, ...1000]
And prop map has a growing number of props, like tags, by which a user can mark colors and show/hide them on filter. Or at some point new prop states will be added.
prop1 0, 1, 2, ...10
prop2 0, 1, 2, ...10
prop3 0, 1, 2, ...10
...
prop100 0, 1, 2, ...10
Should this kind of problem be solved via graph or matrix algorithms or some other method respectively? And, if yes, to what I should look into?
My code for optimisation and efficiency concerns:
const propMap = [
{ name: 'prop1', value: 0, items: [] },
{ name: 'prop2', value: 1, items: [ { id: 1}, { id: 2} ] },
{ name: 'propN', value: 2, items: [ { id: 2} ] },
];
const someArr = [
{ id: 1}, { id: 2}, { id: 3}, { id: 4},{ id: 5},
]
function filterByPropMap (arr) {
// Filter hidden from array
propMap.forEach(prop => {
if (prop.value === 2) {
arr = _.differenceBy(arr, prop.items, 'id');
}
});
// Filter intersecting objects to show
propMap.forEach(prop => {
if (prop.value === 1) {
arr = _.intersectionBy(arr, prop.items, 'id');
}
});
return [...arr];
}
console.log(filterByPropMap(someArr));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
In general, you very often don't need Lodash. Consider the following, which uses only vanilla ES6.
It returns { id: 1 } twice, because it looks like the deduplication is an unintended side effect of your code. At least you never do so explicitly.
const propMap = [
{ name: 'prop1', value: 0, items: [] },
{ name: 'prop2', value: 1, items: [ { id: 1}, { id: 2} ] },
{ name: 'propN', value: 2, items: [ { id: 2} ] },
];
const someArr = [
{ id: 1}, { id: 2}, { id: 3}, { id: 4},{ id: 1},
];
function filterByPropMap(arr) {
const hiddenItems = propMap
.filter(p => p.value === 2)
.map(p => p.items)
.flat();
const intersectingItems = propMap
.filter(p => p.value === 1)
.map(p => p.items)
.flat();
const isEqual = (a, b) => a.id === b.id;
return arr
.filter(v => !hiddenItems.some(h => isEqual(h, v)) &&
intersectingItems.some(i => isEqual(i, v)));
}
console.log(filterByPropMap(someArr));
I have an array with duplicate items. I want to filter that array to return only unique items, but that items have to be sorted based on how many times they were in initial array.
const initialArr = [
{
id: 1
},
{
id: 1
},
{
id: 2
},
{
id: 1
},
{
id: 3
},
{
id: 3
},
];
const expectedSortedResult = [
{
id: 1
},
{
id: 3
},
{
id: 2
}
]
Try to always post your attempt, no matter how far away from the solution it is.
You should research the following (and I solved it with these too):
Reduce (create object, groupBy and create __count property): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
Convert this back to an array with Object.values(), Followed by
Sort (sort by __count): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Then you will need to delete that count property if you don't want it in your output, you can do this with Map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
const initialArr = [
{id: 1},
{id: 1},
{id: 2},
{id: 1},
{id: 3},
{id: 3},
];
const output = Object.values(initialArr.reduce((aggObj, item) => {
if (aggObj[item.id]){
aggObj[item.id].__count += 1
}
else{
aggObj[item.id] = item;
aggObj[item.id].__count = 1
}
return aggObj;
}, {}))
.sort((a,b) => b.__count - a.__count)
.map(a => {delete a.__count; return a});
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I need to add properties to an array of objects from a separate array.
I have 2 arrays. The first array is an array of objects. The second array is an array of Ints. I want to add a new property to each object in the array from the array of Ints.
Example:
var arrOfObj = [
{
name: "eve"
},
{
name: "john"
},
{
name: "jane"
}
];
var arr = [0, 1, 2]
//Desired output
var arrOfObj = [
{
name: "eve",
num: 0
},
{
name: "john",
num: 1
},
{
name: "jane",
num: 2
}
];
Thanks for the help!
You could do with Array#forEach
var arrOfObj = [ { name: "eve" }, { name: "john" }, { name: "jane" }];
var arr = [0, 1, 2];
arrOfObj.forEach((a,i)=>{ a.num = arr[i]});
console.log(arrOfObj)
This question already has an answer here:
Filter array of object from another array
(1 answer)
Closed 3 years ago.
I want to filter an array of objects using an array but I want the results on the basis of array index and the result should be repeated when the array index value is repeated.
const data = [{
id='1',
name:'x'
},
{
id='4',
name:'a'
},
{
id='2',
name:'y'
},
{
id='3',
name:'z'
}
]
cons idArray = [1,4,3,2,4,3,2]
I have tried following code and get the result only once
const filteredData = data.filter(arrayofObj => idArray.includes(arrayofObj.id))
console.log(filteredData)
expected output is
expected output is =
[{id = '1,name:'x'},{id='4',name:'a'},{
id='3',
name:'z'
},
{
id='2',
name:'y'
},{
id='4',
name:'a'
},
{
id='3',
name:'z'
},{
id='2',
name:'y'
}]
First convert data array into Object with id's as keys.
Second, use map method over idArray and gather objects from above object.
const data = [
{
id: "1",
name: "x"
},
{
id: "4",
name: "a"
},
{
id: "2",
name: "y"
},
{
id: "3",
name: "z"
}
];
const dataObj = data.reduce((acc, curr) => {
acc[curr.id] = { ...curr };
return acc;
}, {});
const idArray = [1, 4, 3, 2, 4, 3, 2];
const results = idArray.map(id => ({ ...dataObj[id] }));
console.log(results);
You could map with a Map.
const
data = [{ id: '1', name: 'x' }, { id: '4', name: 'a' }, { id: '2', name: 'y' }, { id: '3', name: 'z' }],
idArray = [1, 4, 3, 2, 4, 3, 2],
result = idArray.map(Map.prototype.get, new Map(data.map(o => [+o.id, o])));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }