My object which maps student id with marks is as follows:
[
{id: 111, marks: [{sub: 'eng', mark: 90}, {sub: 'maths', mark: 20}]},
{id: 222},
{id: 333, marks: []},
{id: 444, marks: [{sub: 'eng', mark: 70}]}
]
I would like to reduce it as follows:
{
marks[0]: "0:eng:90", // studentIndex:subject_name:mark
marks[1]: "0:maths:20",
marks[2]: "3:eng:70"
}
In the above result, the key is "marks[]" and the value is a string which is a concatenation of studentIndex, the subject and the mark.
So here 0:eng:90 means that student with index of 0 has obtained 90 marks in english subject
I am using lodash and I have tried the following:
reduce(studentList, (acc, student, studentIndex) => {
map(get(student, 'marks'), (marks) => {
acc[`marks[${keys(acc).length}]`] = `${studentIndex}:${marks.sub}:${marks.mark}`;
});
return acc;
}, {});
Is there any other better way to do this?
Without Lodash
var studentList = [{"id":111,"marks":[{"sub":"eng","mark":90},{"sub":"maths","mark":20}]},{"id":222},{"id":333,"marks":[]},{"id":444,"marks":[{"sub":"eng","mark":70}]}]
var result = studentList.reduce((acc, student, studentIndex) => {
(student.marks || []).map((marks) => {
acc[`marks[${Object.keys(acc).length}]`] = `${studentIndex}:${marks.sub}:${marks.mark}`;
});
return acc;
}, {});
console.log(result)
With Lodash
var studentList = [{"id":111,"marks":[{"sub":"eng","mark":90},{"sub":"maths","mark":20}]},{"id":222},{"id":333,"marks":[]},{"id":444,"marks":[{"sub":"eng","mark":70}]}]
var result = _.reduce(studentList, (acc, student, studentIndex) => {
_.map(student.marks || [], (marks) => {
acc[`marks[${_.keys(acc).length}]`] = `${studentIndex}:${marks.sub}:${marks.mark}`;
});
return acc;
}, {});
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
You can use forEach loop.
const input = [
{id: 111, marks: [{sub: 'eng', mark: 90}, {sub: 'maths', mark: 20}]},
{id: 222, marks: []},
{id: 333, marks: []},
{id: 444, marks: [{sub: 'eng', mark: 70}]}
];
const output = [];
input.forEach(({marks}, i) => {
marks.forEach(({sub, mark}) => {
output.push(`${i}:${sub}:${mark}`);
});
});
console.log(output);
--Edit--
const input = [
{id: 111, marks: [{sub: 'eng', mark: 90}, {sub: 'maths', mark: 20}]},
{id: 222},
{id: 333, marks: []},
{id: 444, marks: [{sub: 'eng', mark: 70}]}
];
const output = {};
let count = 0;
input.forEach((obj, i) => {
if(obj.hasOwnProperty("marks")) {
obj.marks.forEach(({sub, mark}) => {
output[`marks[${count}]`] = `${i}:${sub}:${mark}`;
count++;
});
}
});
console.log(output);
Another shorter way without lodash using map:
var studentList = [
{id: 111, marks: [{sub: 'eng', mark: 90}, {sub: 'maths', mark: 20}]},
{id: 222},
{id: 333, marks: []},
{id: 444, marks: [{sub: 'eng', mark: 70}]}
];
var ac = {};
studentList.map((student, i) => {
if (student.marks && student.marks.length)
student.marks.map(m => ac[`marks[${Object.keys(ac).length}]`] = `${i}:${m.sub}:${m.mark}`);
});
console.log(ac);
With lodash you can iterate the students with _.flatMap(), and map the students' marks to pairs of [key, value]. To generate the key you can use _.uniqueId(). The end result would be an array of pairs, which you can convert to an object with _.fromPairs():
const studentList = [{"id":111,"marks":[{"sub":"eng","mark":90},{"sub":"maths","mark":20}]},{"id":222},{"id":333,"marks":[]},{"id":444,"marks":[{"sub":"eng","mark":70}]}]
const result = _.fromPairs( // convert the array to an object
_.flatMap(studentList, ({ marks }, sIndex) => // iterate the students
_.map(marks, ({ sub, mark }) => [ // iterate the marks
`marks${_.uniqueId() - 1}`, // generate the key
`${sIndex}:${sub}:${mark}` // generate the value
])
)
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Related
I need to filter an array of objects
const arrayOne = [
{id: 33, name: "fruit"},
{id: 157, name: "car"},
{id: 193, name: "water"},
];
const arrayTwo = [33, 193];
I need only the names in an array (without the key)
Expected output
["fruit", "water"]
1) You can easily achive the result using Map
const arrayOne = [
{ id: 33, name: "fruit" },
{ id: 157, name: "car" },
{ id: 193, name: "water" },
];
const map = new Map();
arrayOne.forEach((o) => map.set(o.id, o.name));
const arrayTwo = [33, 193];
const result = arrayTwo.map((o) => map.get(o));
console.log(result);
2) You can also achieve the result using map and find
const arrayOne = [
{ id: 33, name: "fruit" },
{ id: 157, name: "car" },
{ id: 193, name: "water" },
];
const arrayTwo = [33, 193];
const result = arrayTwo.map((id) => arrayOne.find((o) => o.id === id)?.name);
console.log(result);
you can just simply do
const arrayOne = [
{ id: 33, name: "fruit" },
{ id: 157, name: "car" },
{ id: 193, name: "water" }
];
const arrayTwo = [33, 193];
let keepResult = [];
arrayOne.map((a1) => {
arrayTwo.map((a2) => {
if (a1.id === a2) {
keepResult.push(a1.name);
}
});
});
console.log("result show", keepResult);
const arrayOne = [
{id: 33, name: "fruit"},
{id: 157, name: "car"},
{id: 193, name: "water"},
];
const arrayTwo = [33, 193];
let newArray = arrayOne.filter(item => arrayTwo.includes(item.id)).map(item => item.name)
console.log(newArray);
try this
this function is your solution:
const findNames = () => {
let names = []
arrayOne.forEach(item => {
if (arrayTwo.includes(item.id)) {
names = [...names, item.name]
}
})
return names
}
Here is your compare() function for getting array of names based on two array
const arrayOne = [
{id: 33, name: "fruit"},
{id: 157, name: "car"},
{id: 193, name: "water"},
];
const arrayTwo = [33, 157];
function compare(arrayOne, arrayTwo) {
let result = [];
arrayOne.filter(element => arrayTwo.indexOf(element.id) !== -1 ).forEach(element => { result.push(element.name) });
return result ;
}
Use a flatmap on the first array and include only those members having an id property included in array two.
arrayOne.flatMap(({ id, name }) => arrayTwo.includes(id) ? [name]: []);
const arrayOne = [
{id: 33, name: "fruit"},
{id: 157, name: "car"},
{id: 193, name: "water"},
];
const arrayTwo = [33, 193];
const result = arrayOne.flatMap(({ id, name }) => arrayTwo.includes(id) ? [name]: []);
console.log(result);
I have an array that I find in the searchName section, and in the resultName section I separate the duplicate names, and in the filters section I want to display the objects that have those duplicate names in the console.log, but an empty array
Please help me to get the answer
const data = [
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Noah",age: 22},
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Elijah",age: 18},
{id: 1,name: "Elijah",age: 18}
]
const searchName = data.map(item => item.name)
console.log(searchName);
const toFindDuplicates = arry => arry.filter((item, index) => arry.indexOf(item) !== index);
const resultName = toFindDuplicates(searchName)
console.log(resultName);
const filters = data.filter(x => x.Name === resultName)
console.log(filters);
Use .includes() to check if the name of an element exists inside the resultName array. You also had a typo: x.name instead of x.Name
const data = [{
id: 1,
name: "Liam",
age: 20
},
{
id: 1,
name: "Noah",
age: 22
},
{
id: 1,
name: "Liam",
age: 20
},
{
id: 1,
name: "Elijah",
age: 18
},
{
id: 1,
name: "Elijah",
age: 18
}
]
const searchName = data.map(item => item.name)
console.log(searchName);
const toFindDuplicates = arry => arry.filter((item, index) => arry.indexOf(item) !== index);
const resultName = toFindDuplicates(searchName)
console.log(resultName);
const filters = data.filter(x => resultName.includes(x.name))
console.log(filters);
You need to fix your filter function.
In your version you trying to compare an array to an string.
What you need to do is something like this:
const filters = data.filter(x => resultName.includes(x.name))
First you can count the names and then fill the array
const data = [
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Noah",age: 22},
{id: 1,name: "Liam",age: 20},
{id: 1,name: "Elijah",age: 18},
{id: 1,name: "Elijah",age: 18}
]
const obj = data.reduce((acc, el) => { acc[el.name] = (acc[el.name] ?? 0) + 1; return acc; }, {});
const all = Object.keys(obj);
const duplicates = Object.entries(obj).filter(el => el[1] > 1).map(el => el[0]);
const uniques = Object.entries(obj).filter(el => el[1] === 1).map(el => el[0]);
console.log(all);
console.log(duplicates);
console.log(uniques);
This question already has answers here:
Using array map to filter results with if conditional
(5 answers)
Map and filter an array at the same time
(16 answers)
Closed 3 years ago.
I have an object as shown below:
var obj = [
{id: 1, name: 'AD', key: 10},
{id: 2, name: 'AD', key: 20},
{id: 3, name: 'BD', key: 30},
{id: 4, name: 'CD', key: 40}
];
I want to filter and create a new array which satisfies any condition. For eg: filter which have name as 'AD' and create a new array of key as:
[10, 20]
Tried .map
obj.map(ele => {
return ele.name === 'AD' ? ele.key : null;
}); //it adds even nulls in the result array as [10, 20, null, null]
Tried .filter:
obj.filter(ele => {
return ele.name === 'AD' ? ele.key : null;
});
Result: [{id: 1, name: "AD", key: 10}, {id: 2, name: "AD", key: 20}] //gives array of objects, not what I'm expecting.
Thanks in advance
First filter the array and then map to get the value:
obj.filter(e => e.name === 'AD').map(e => e.key)
Another option is flatmap (check browser compatibility here)
obj.flatMap(e => e.name === 'AD' ? [e.key] : [])
First of all obj is an array in your code.
Now the solution is simple you filter first and then map like this:
obj.filter(item => item.name === 'AD').map(item => item.key);
You can use reduce():
var obj = [{id: 1, name: 'AD', key: 10},{id: 2, name: 'AD', key: 20},{id: 3, name: 'BD', key: 30},{id: 4, name: 'CD', key: 40}];
var result = obj.reduce((acc, cur) => {
if (cur.name == "AD") acc.push(cur.key)
return acc;
}, []);
console.log(result);
var obj = [
{id: 1, name: 'AD', key: 10},
{id: 2, name: 'AD', key: 20},
{id: 3, name: 'BD', key: 30},
{id: 4, name: 'CD', key: 40}
];
function specialFilter(filt) {
return obj.filter(el => el.name === filt).map(el => el.key)
}
console.log(specialFilter("AD"))
I have two arrays like this:
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
Now I want a result like this:
result = [ {name: "apple", prices: [{id: 1, price: 50}, {id: 1, price: 40}]}, {name: "orange", prices: [{id: 2, price: 30}]}, {name: "others", prices: [{id: null, price: 80}]}]
I want to map the elements of the array a to the name of the second array b on the basis of their ids.
Here's an approach that using reduce to build a lookup set and avoid repeated searches in b. Another reduction pass builds the result arrays by name using the lookup table. Lastly, map is used to format the result.
Time complexity is linear (three passes with a lot of constant time object lookups).
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const lookup = b.reduce((a, e) => {
a[e.id] = e.name;
return a;
}, {});
const result = Object.entries(
a.reduce((a, e) => {
const key = lookup[e.id] || "others";
if (!(key in a)) {
a[key] = [];
}
a[key].push(e);
return a;
}, {})
).map(e => ({name: e[0], prices: e[1]}));
console.log(result);
It would be more logical to not repeat the id in the prices part of the result, since the id belongs with the name.
I would suggest using a temporary map (for efficiency):
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const map = new Map(b.map(o => [o.id, Object.assign(o, { prices: [] })]))
.set(null, {id: null, name: "others", prices: []});
a.forEach(o => map.get(o.id).prices.push(o.price));
const result = [...map.values()];
console.log(result);
Yes, you can do it simply with a map and a filter
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
b.map(({ name, id }) => ({
name,
id,
prices: a.filter(item => item.id === id).map(({ price }) => price)
}));
You can do this with a single Array.reduce and Object.values if you start by combining the 2 arrays together:
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const result = Object.values([...b, ...a].reduce((r, c) => {
if ('name' in c || c.id == null)
r[c.id || 'others'] = ({name: c.name || 'others', prices: []})
if ('name' in c)
return r
else if (c.id != null)
r[c.id].prices.push(c)
else
r['others'].prices.push(c)
return r
}, {}))
console.log(result)
The idea is to start with the one containing the names so the grouping creates first the object groupings and then just fill the group arrays.
let array1 = [{id: 562, name: "Eva Bridal"},{id: 582, name: "Finger Touch"},]
let array2 = [{id: 575, name: "Saloon Grand"} , {id: 562, name: "Eva Bridal"}]
Final Output :
let newarray = [{id: 582, name: "Finger Touch"},{id: 575, name: "Saloon Grand"} ]
Add all items from array1, and those items from array2 that are not found in array1 to a resulting array.
This uses spread syntax in an array literal, and .filter() with .find() to exclude the duplicates.
let array1 = [{id: 562, name: "Eva Bridal"},{id: 582, name: "Finger Touch"},];
let array2 = [{id: 575, name: "Saloon Grand"} , {id: 562, name: "Eva Bridal"}];
var result = [
...array1.filter(o => !array2.find(o2 => o.id == o2.id)),
...array2.filter(o => !array1.find(o2 => o.id == o2.id))
];
console.log(result);
I assume that a duplicate can appear more than once (ie 3 items with the same id). If this is the case, you can use a Set to hold existing ids. Concat the two arrays, and use reduce to create a Map. If an item's id exists in the Set, remove it from Map, if not add the item to the Map, and it's id to the Set. When the reduce process ends, get the Map values, and spread to an array:
const array1 = [{id: 562, name: "Eva Bridal"},{id: 582, name: "Finger Touch"}]
const array2 = [{id: 575, name: "Saloon Grand"}, {id: 562, name: "Eva Bridal"}]
const existing = new Set();
const result = [...array1.concat(array2).reduce((r, o) => {
if(existing.has(o.id)) {
r.delete(o.id);
} else {
existing.add(o.id);
r.set(o.id, o);
}
return r;
}, new Map).values()];
console.log(result);
If you are sure that there will only be pairs of duplicated items (ie - no more than 2 items with the same id), use just the Map, and remove an item if it already exists in the Map:
const array1 = [{id: 562, name: "Eva Bridal"},{id: 582, name: "Finger Touch"}]
const array2 = [{id: 575, name: "Saloon Grand"}, {id: 562, name: "Eva Bridal"}]
const result = [...array1.concat(array2).reduce((r, o) => {
if(r.has(o.id)) {
r.delete(o.id);
} else {
r.set(o.id, o);
}
return r;
}, new Map).values()];
console.log(result);