How to reduce an array with duplicate value? - javascript

I have an array like this
let oldArray=[
{type:16,img:['1']},
{type:16,img:['2']},
{type:16,img:['3']},
{type:17,img:['4']}
]
if the type is the same, i want to concat the value.
The result I want is:
let newArray=[
{type:16,img:['1','2','3']},
{type:17,img:['4']}
]
I tried to used reduce function:
oldArray.reduce((acc,cur,idx,src)=>{
if(cur.type===a[idx+1].type){
cur.img.concat(a[idx+1].img);
acc.push(cur)
} else {
acc.push(a[idx+1])
}
return acc
},[])
It seems that there is an error
Can anyone help? Thanks.
Alternative to Bibberty's solution:flatMap is much clearer than reduce
let newArray = [...new Set(oldArray.map(e => e.type))]
.map(e => {
return {
type: e,
img: (oldArray.filter(i => i.type === e).map(x => x.img)).reduce((acc,cur,idx,src)=>{
let length=src.length
let tep=cur.concat(src[idx+1]);
src[idx+1]=tep
return src[idx=length-1]
},[])
}
});
console.log(newArray);

You can use reduce:
let oldArray = [{type: 16,img: ['1']},{type: 16,img: ['2']},{type: 16,img: ['3']},{type: 17,img: ['4']}];
let newArray = oldArray.reduce((acc, curr) => {
acc.some(({
type
}) => type == curr.type) ? acc.find(({
type
}) => type == curr.type).img.push(curr.img[0]) : acc.push(curr);
return acc;
}, []);
console.log(newArray);

We use a Set and then a map.
The Set is populate with the unique types by using a map to extract.
We wrap in [] to give us an array the we then re map to build our object back.
The map then rebuilds our objects and note the use of filter and map to get the img values from the original host array.
let oldArray=[
{type:16,img:['1']},
{type:16,img:['2']},
{type:16,img:['3']},
{type:17,img:['4']}
]
let newArray = [...new Set(oldArray.map(e => e.type))]
.map(e => {
return {
type: e,
img: oldArray.filter(i => i.type === e).flatMap(x => x.img)
}
});
console.log(newArray);

This solution is not a reduce but return result you are looking for is the same
let oldArray = [
{type:16,img:['1']},
{type:16,img:['2']},
{type:16,img:['3']},
{type:17,img:['4']}
];
const transitoryMap = new Map();
for (const item of oldArray) {
if (!transitoryMap.has(item.type)) {
transitoryMap.set(item.type, [item.img[0]])
} else {
const value = transitoryMap.get(item.type)
value.push(item.img[0])
transitoryMap.set(item.type, value)
}
}
const newArray = [];
for (const item of transitoryMap.keys()) {
newArray.push({type:item,img:transitoryMap.get(item)})
}
console.log(newArray)

Here is an example using reduce. I have added a tracker to keep track of type in the newArray.
let oldArray = [
{type:16,img:['1']},
{type:16,img:['2']},
{type:16,img:['3']},
{type:17,img:['4']}
];
oldArray.reduce((a,c)=>{
let index = a.tracker.indexOf(c.type);
if(index === -1) {
a.tracker.push(c.type);
a.newArray.push({...c, img:[...c.img]});
} else {
a.newArray[index].img.push(...c.img);
}
return a;
},{tracker:[],newArray:[]}).newArray;

You might want to consider breaking up the processing into separate simple steps, for example:
Create a flattened object with the appropriate data.
build a new array with the wanted structure.
This will not only keep your code simple, but will allow you to focus on what your code is actually doing instead of how it is doing the task.
var oldArray=[
{type:16,img:['1']},
{type:16,img:['2']},
{type:16,img:['3']},
{type:17,img:['4']}
]
flattenMyObject = (arr) =>
arr.reduce((accum, current) => {
!!accum[current.type] ? accum[current.type].push(...current.img) : accum[current.type] = current.img;
return accum;
}, {});
buildNewArray = (type) => {
return {type: type, img: flattenedObject[type] }
}
Object
.keys(flattenMyObject(oldArray))
.map(buildNewArray);

Related

convert array of objects to map of field values structure javascript

I have an array of objects like this:
const arr=[
{ name:"test",
class:3
},
{ name:"test2",
class:4
},
{ name:"test3",
class:5
},]
Now I have to convert it to a map like structure as shown below:
const map={
"name":["test","test2","test3"],
"class":[3,4,5]
}
I am clueless on how to make this kind of structure.Any leads will be appreciated.
If you have an arbitrary amount of keys you can use Object.entries()
to get all the key value pairs. Then just loop over all entries and add them to the final output.
const arr=[{name:"test",class:3},{name:"test2",class:4},{name:"test3",class:5}];
const map = arr.reduce((acc, obj) => {
for(const [key, value] of Object.entries(obj)) {
if(acc[key]) {
acc[key].push(value);
}else{
acc[key] = [value];
}
}
return acc;
}, {});
console.log(map);
Have a function that accepts the array and the property you're looking for, and return a new array of data using map for each property of your new object.
const arr=[{name:"test",class:3},{name:"test2",class:4},{name:"test3",class:5}];
function getData(arr, prop) {
return arr.map(obj => obj[prop]);
}
const map = {
name: getData(arr, 'name'),
class: getData(arr, 'class')
}
console.log(map);
It should be something like that
arr.reduce((res, obj) => {
Object.entries(obj).forEach(([k,v]) => {
if(!res[k]) res[k] = []
res[k].push(v)
})
return res
},{})

Adding a Json Object after a particular Object in array in javascript

I have an array of objects which looks like
arr = [
{"className":"section", "name":"input"},
{"className":"col", "name":"dropdown"},
{"className":"section", "name":"table"}
]
Now I want to push an object {"fieldName":"new"} after every object which has "className":"section"
So my final output should look like something like this
arr = [
{"className":"section", "name":"input"},
{"fieldName":"new"},
{"className":"col","name":"dropdown"},
{"className":"section", "name":"table"},
{"fieldName":"new"}]
How do I acheive this in Javascript?
Use Array.reduce.
let arr = [{"className":"section", "name":"input"},{"className":"col", "name":"dropdown"},{"className":"section", "name":"table"}];
arr = arr.reduce((acc, item) => {
acc.push(item);
if (item.className === "section") {
acc.push({ fieldName: "new" });
}
return acc;
}, []);
console.log(arr);
try this...
arr.forEach(obj => {
if (obj.className === 'section') {
const index = arr.indexOf(obj)
arr.splice(index, 0,
{"fieldName":"new"} )
}
})

How to use the result of an iteration to re-iterate?

I need to create a new array from another with the condition:
for example from an array
mainArr: [
{
"id":1,
"name":"root"
},
{
"id":2,
"parentId":1,
"name":"2"
},
{
"id":148,
"parentId":2,
"name":"3"
},
{
"id":151,
"parentId":148,
"name":"4"
},
{
"id":152,
"parentId":151,
"name":"5"
}
]
I need to make an array ['1','2','148','151'] which means the path from "parentId"'s to "id":152 - (argument for this function).
I think main logic can be like this:
const parentsArr = [];
mainArr.forEach((item) => {
if (item.id === id) {
parentsArr.unshift(`${item.parentId}`);
}
and the result {item.parentId} should be used to iterate again. But I don't understand how to do it...
You could use a recursive function for this. First you can transform your array to a Map, where each id from each object points to its object. Doing this allows you to .get() the object with a given id efficiently. For each object, you can get the parentId, and if it is defined, rerun your traverse() object again searching for the parent id. When you can no longer find a parentid, then you're at the root, meaning you can return an empty array to signify no parentid object exist:
const arr = [{"id":1,"name":"root"},{"id":2,"parentId":1,"name":"2"},{"id":148,"parentId":2,"name":"3"},{"id":151,"parentId":148,"name":"4"},{"id":152,"parentId":151,"name":"5"}];
const transform = arr => new Map(arr.map((o) => [o.id, o]));
const traverse = (map, id) => {
const startObj = map.get(+id);
if("parentId" in startObj)
return [...traverse(map, startObj.parentId), startObj.parentId];
else
return [];
}
console.log(traverse(transform(arr), "152"));
If you want to include "152" in the result, you can change your recursive function to use the id argument, and change the base-case to return [id] (note that the + in front of id is used to convert it to a number if it is a string):
const arr = [{"id":1,"name":"root"},{"id":2,"parentId":1,"name":"2"},{"id":148,"parentId":2,"name":"3"},{"id":151,"parentId":148,"name":"4"},{"id":152,"parentId":151,"name":"5"}];
const transform = arr => new Map(arr.map((o) => [o.id, o]));
const traverse = (map, id) => {
const startObj = map.get(+id);
if("parentId" in startObj)
return [...traverse(map, startObj.parentId), +id];
else
return [+id];
}
console.log(traverse(transform(arr), "152"));
I would start by indexing the data by id using reduce
var byId = data.reduce( (acc,i) => {
acc[i.id] = i
return acc;
},{});
And then just go through using a loop and pushing the id to a result array
var item = byId[input];
var result = []
while(item.parentId) {
result.push(item.parentId)
item = byId[item.parentId];
}
Live example:
const input = 152;
const data = [ { "id":1, "name":"root" }, { "id":2, "parentId":1, "name":"2" }, { "id":148, "parentId":2, "name":"3" }, { "id":151, "parentId":148, "name":"4" }, { "id":152, "parentId":151, "name":"5" } ]
var byId = data.reduce( (acc,i) => {
acc[i.id] = i
return acc;
},{});
var item = byId[input];
var result = []
while(item.parentId) {
result.push(item.parentId)
item = byId[item.parentId];
}
console.log(result.reverse());
Try changing this line
parentsArr.unshift(`${item.parentId}`);
To this
parentsArr.push(`${item.parentId}`);
Then try
console.log(parentsArr);
This is what I ended up with. Basically a mix of Janek and Nicks answers. It's just 2 steps:
transform code to a map.
extract the ancester_id's with a little function
let data = [
{"id":1,"name":"root"},
{"id":2,"parentId":1,"name":"2"},
{"id":148,"parentId":2,"name":"3"},
{"id":151,"parentId":148,"name":"4"},
{"id":152,"parentId":151,"name":"5"}
];
data = data.reduce( (acc, value) => {
// could optionally filter out the id here
return acc.set(value.id, value)
}, new Map());
function extract_ancestors( data, id ) {
let result = [];
while( data.get( id ).parentId ) {
id = data.get( id ).parentId;
result.push(id)
}
return result;
}
// some visual tests
console.log( extract_ancestors( data, 152 ) );
console.log( extract_ancestors( data, 148 ) );
console.log( extract_ancestors( data, 1 ) );
PS: My OOP tendencies start to itch so much from this haha.

How can I do total count of values accordingly selected feild in array

I have a array of objects, for exemple:
let arr = [
{title:apple,quantity:2},
{title:banana,quantity:3},
{title:apple,quantity:5},
{title:banana,quantity:7}
];
array containe many same objects, and i want recived array with uniqe object :
let result = [
{title:apple,quantity:7},
{title:banana,quantity:10}
]
How can I do this?
You can iterate over your array and filter out all the object with same title. Then use reduce to add all the quantity and return a new object. Code is below,
let newArr = [];
arr.forEach((currentObj) => {
const alreadyExists = newArr.findIndex(item => currentObj.title === item.title) > -1;
if(!alreadyExists) {
const filtered = arr.filter(item => item.title === currentObj.title);
const newObject = filtered.reduce((acc, curr) => { return {...acc, quantity: acc.quantity += curr.quantity}}, {...currentObj, quantity: 0})
newArr.push(newObject);
}
})
console.log(newArr);
This is done on a phone so may have some typos but the gist is there:
const resultObj = arr.reduce((acc,curr) =>{
acc[curr.title] = acc[curr.title]== undefined? curr.quantity: acc[curr.title] + curr.quantity
return acc
},{})
const resultArr = Object.entries(resultObj).map([key,value]=>({title:key,quantity:value}))
You could do that in "one line" using arrow function expressions but it won't be very readable unless you know what's happening inside:
let arr = [
{title: "apple",quantity:2},
{title: "banana",quantity:3},
{title: "apple",quantity:5},
{title: "banana",quantity:7}
];
let newArr = [...arr.reduce((acc, {title, quantity}) =>
(acc.set(title, quantity + acc.get(title) || 0), acc), new Map())
].map(([title, quantity]) => ({title, quantity}));
console.log(newArr);
So basically the first part is the reduce method:
arr.reduce((acc, {title, quantity}) =>
(acc.set(title, quantity + acc.get(title) || 0), acc), new Map())
That will returns a Map object, where each title is a key (e.g. "apple") and the quantity is the value of the key.
At this point you have to convert the Map object into an array again, and you do it using the spread syntax.
After you got an array back, you will have it in the following form:
[["apple", 7], ["banana", 10]]
But that is not what you want yet, not in this form, so you have to convert it using the array's map method:
<array>.map(([title, quantity]) => ({title, quantity}))
To keep it concise it uses the destructuring assignment

Reduce an array to a specific structure using javascript

I have an array of the objects
[
{"id":"brand","label":"Brand","value":"value1"},
{"id":"waterproof","label":"Waterproof","value":"value1"},
{"id":"diameter","label":"Diameter","value":""},
{"id":"brand","label":"Brand","value":"value2"},
{"id":"waterproof","label":"Waterproof","value":"value2"},
{"id":"diameter","label":"Diameter","value":""}
]
I need to restructure it to a following structure,
[
["Brand", "value1", "value2"],
["Waterproof", "value1","value2"],
["Diameter","",""]
]
Any ideas on how I could do this using a reduce method.
Best
Try following using Array.reduce and Object.values
let arr = [{"id":"brand","label":"Brand","value":"value1"},{"id":"waterproof","label":"Waterproof","value":"value1"},{"id":"diameter","label":"Diameter","value":""},{"id":"brand","label":"Brand","value":"value2"},{"id":"waterproof","label":"Waterproof","value":"value2"},{"id":"diameter","label":"Diameter","value":""}];
let result = Object.values(arr.reduce((a,c) => {
if(a[c.id]) a[c.id].push(c.value);
else a[c.id] = [c.label, c.value];
return a;
}, {}));
console.log(result);
Yust take a Map and collect all values.
var json = '[{"id":"brand","label":"Brand","value":"value1"},{"id":"waterproof","label":"Waterproof","value":"value1"},{"id":"diameter","label":"Diameter","value":""},{"id":"brand","label":"Brand","value":"value2"},{"id":"waterproof","label":"Waterproof","value":"value2"},{"id":"diameter","label":"Diameter","value":""}]',
result = Array.from(JSON
.parse(json)
.reduce((m, { id, label, value }) => m.set(id, (m.get(id) || [label]).concat(value)), new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Not the best of code but i think this might be useful for you:
let data = [
{"id":"brand","label":"Brand","value":"value1"},
{"id":"waterproof","label":"Waterproof","value":"value1"},
{"id":"diameter","label":"Diameter","value":""},
{"id":"brand","label":"Brand","value":"value2"},
{"id":"waterproof","label":"Waterproof","value":"value2"},
{"id":"diameter","label":"Diameter","value":""}
]
let reducedData = {}
data.forEach((row)=>{
reducedData[row.id] ? reducedData[row.id].push(row.value) : reducedData[row.id] = [row.label, row.value]
})
let newData = Object.keys(reducedData).map((id)=>{return [...reducedData[id]]})
You can also use a simple combo of Array.prototype.reduce and Object.keys:
const data = [{"id":"brand","label":"Brand","value":"value1"},{"id":"waterproof","label":"Waterproof","value":"value1"},{"id":"diameter","label":"Diameter","value":""},{"id":"brand","label":"Brand","value":"value2"},{"id":"waterproof","label":"Waterproof","value":"value2"},{"id":"diameter","label":"Diameter","value":""}];
const grouped = data.reduce((o, { label, value }) =>
({...o, [label]: [...(o[label] || []), value]}), {});
const result = Object.keys(grouped).map(label => [label, ...grouped[label]]);
console.log(result);
Here I use Set to create a unique list of id's then I map to create a custom array based on those id's. This is done by using find to get the label, and filter to get all the items that are related to the id. I map again on the filter to just return the value of the object.
let items = [
{"id":"brand","label":"Brand","value":"value1"},
{"id":"waterproof","label":"Waterproof","value":"value1"},
{"id":"diameter","label":"Diameter","value":""},
{"id":"brand","label":"Brand","value":"value2"},
{"id":"waterproof","label":"Waterproof","value":"value2"},
{"id":"diameter","label":"Diameter","value":""}
]
let result = [...new Set(items.map(i => i.id))]
.map(id => {
return [
items.find(i => i.id == id).label,
...items.filter(i => i.id == id).map(i => i.value)
]
})
console.log(result)
In case the JSON Array is a very long one from some API
var data = [
{"id":"brand","label":"Brand","value":"value1"},
{"id":"waterproof","label":"Waterproof","value":"value1"},
...... //some JSON array form some API
];
var json = JSON.stringify(data);
result = Array.from(JSON
.parse(json)
.reduce((m, { id, label, value }) => m.set(id, (m.get(id) || [label]).concat(value)), new Map)
.values()
);
console.log(result);

Categories

Resources