I want to sort my current array by having live at the front, instead of sorting it alphabetically so that the items with live will be at the front, followed by schedule and lastly end, but the way I'm doing it now with localeCompare, it will only sort alphabetically.
So how can I do so to sort the array in a specific way that I want?
Array Before Sorting Example
const [bulletins] = useState([
{
id: 1,
liveStatus: 'live',
},
{
id: 2,
liveStatus: 'live',
},
{
id: 3,
liveStatus: 'end',
},
{
id: 4,
liveStatus: 'schedule',
},
{
id: 5,
liveStatus: 'end',
}
]);
Sorted Array Example
const [bulletins] = useState([
{
id: 3,
liveStatus: 'end',
},
{
id: 5,
liveStatus: 'end',
},
{
id: 2,
liveStatus: 'live',
},
{
id: 1,
liveStatus: 'live',
},
{
id: 4,
liveStatus: 'schedule',
}
]);
const displayBulletins = bulletins
.sort((a,b) => {
return a.liveStatus.localeCompare(b.liveStatus)
})
.map((bulletin) => {
return (
<Fragment key={bulletin.id}>
<BulletinList
bulletin={bulletin}
/>
</Fragment>
);
})
You can use indexOf to check the first letter in a list of sorted letters ('lse' in this case):
const bulletins = [{id: 1,liveStatus: 'live',},{id: 2,liveStatus: 'live',},{id: 3,liveStatus: 'end',},{id: 4,liveStatus: 'schedule',},{id: 5,liveStatus: 'end',}];
bulletins.sort((a, b) => 'lse'.indexOf(a.liveStatus[0]) - 'lse'.indexOf(b.liveStatus[0]));
console.log(bulletins);
Then no need to sort the elements, all you can use is reduce and arrange in the way you like
const arr = [
{
id: 1,
liveStatus: "live",
},
{
id: 2,
liveStatus: "live",
},
{
id: 3,
liveStatus: "end",
},
{
id: 4,
liveStatus: "schedule",
},
{
id: 5,
liveStatus: "end",
},
];
const dict = { live: 0, schedule: 1, end: 2 };
const resultObj = Array.from({ length: Object.keys(dict).length + 1 }, () => []);
const result = arr
.reduce((acc, curr) => {
acc[dict[curr.liveStatus] ?? resultObj.length - 1].push(curr);
return acc;
}, resultObj)
.flat();
console.log(result);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
Related
I'm trying to expand array in JavaScript.
The object ↓
const tests = [
{
id: 1,
name: 'taro',
designs: [
{
designId: 1,
designName: "design1"
},
{
designId: 2,
designName: "design2"
}
]
},
{
id: 2,
name: 'John',
designs: [
{
designId: 3,
designName: "design3"
},
{
designId: 4,
designName: "design4"
}
]
},
{
id: 3,
name: 'Lisa',
designs: []
},
];
[
{ id: 1, name: 'taro', designId: 1, designName: 'design1' },
{ id: 1, name: 'taro', designId: 2, designName: 'design2' },
{ id: 2, name: 'John', designId: 3, designName: 'design3' },
{ id: 2, name: 'John', designId: 4, designName: 'design4' },
{ id: 3, name: 'Lisa', designId: null, designName: null },
]
It is easy to do this using double for, but I want to use it with higher-order functions.
The code I wrote
for (let i = 0; i < tests.length; i++) {
for (let j = 0; j < tests[i].designs.length; j++) {
const id = tests[i].id
const name = tests[i].name
result.push({
id,
name,
designId: tests[i].designs[j].designId,
designName: tests[i].designs[j].designName
})
}
}
In addition, it would be appreciated if you could additionally explain the difference in performance between double for and higher-order functions.
You can use .flatMap() on your tests array with an inner .map() on each designs array. The inner map on the designs array will take the properties from the currently iterated design object and merge it with the properties from the parent object. The outer .flatMap() can then be used to concatenate all returned maps into the one array:
const tests = [ { id: 1, name: 'taro', designs: [ { designId: 1, designName: "design1" }, { designId: 2, designName: "design2" } ] }, { id: 2, name: 'John', designs: [ { designId: 3, designName: "design3" }, { designId: 4, designName: "design4" } ] }, ];
const res = tests.flatMap(({designs, ...rest}) => designs.map(design => ({
...rest,
...design
})));
console.log(res);
Edit:
If you need null values to appear for your design objects if your designs array is empty, you can add the keys explicitly to a new object that you can return when the designs array is empty:
const tests = [ { id: 1, name: 'taro', designs: [] }, { id: 2, name: 'John', designs: [] }, ];
const res = tests.flatMap(({designs, ...rest}) =>
designs.length
? designs.map(design => ({
...rest,
...design
}))
: {...rest, designId: null, designName: null}
);
console.log(res);
You can use an Array.reduce function with Array.map to generate the array:
const results = tests.reduce((acc, { designs, ...rest }) => [
...acc,
...designs.map(e => ({ ...rest, ...e }))
], []);
const tests = [
{
id: 1,
name: 'taro',
designs: [
{
designId: 1,
designName: "design1"
},
{
designId: 2,
designName: "design2"
}
]
},
{
id: 2,
name: 'John',
designs: [
{
designId: 3,
designName: "design3"
},
{
designId: 4,
designName: "design4"
}
]
},
];
const results = tests.reduce((acc, { designs, ...rest }) => [
...acc,
...designs.map(e => ({ ...rest, ...e }))
], []);
console.log(results);
You can use the higher-order function Array.prototype.reduce() with Array.prototype.map()
const newArr = tests.reduce((prev, {designs, ...current}) => [
...prev, ...designs.map(design => ({...design,...current}));
]
, []);
The performance in your approach and this higher-order approach is the same because Array.prototype.reduce runs through the whole array and just facilitates the initialValue approach for us.
<script> var itemsTemp= [
{ id: 0, text: 'Andy' },
{
id: 1, text: 'Harry',
children: [
{ id: 2, text: 'David' }
]
},
{ id: 3, text: 'Lisa' },
{ id: 4, text: 'Mona' },
{ id: 5, text: 'Ron' },
{ id: 6, text: 'Joe' }
];
var items = itemsTemp;
var filtered = items.filter(function(item) {
return item.id !== 3;
});
console.log(filtered);
</script>
in this way, I can only remove the parent but how can I delete the child object? please help me to fix this
Since you want to filter children, you can use .reduce() to perform a mapping and filtering of your array. When you reach an object which has a children property, you can recursively call your function to then perform the mapping/filtering on the child array .reduce() array like so:
const items = [{ id: 0, text: 'Andy' }, { id: 1, text: 'Harry', children: [{ id: 2, text: 'David' }] }, { id: 3, text: 'Lisa' }, { id: 4, text: 'Mona' }, { id: 5, text: 'Ron' }, { id: 6, text: 'Joe' } ];
const filterItems = (items, fn) => items.reduce((acc, item) => {
if(item.children)
return [...acc, ...filterItems(item.children, fn)];
else if(fn(item))
return [...acc, item];
return acc;
}, []);
const filtered = filterItems(items, item => item.id !== 2);
console.log(filtered);
If you don't want to remove the item from the parent list, and only from the child list, then you push an update object instead:
const items = [{ id: 0, text: 'Andy' }, { id: 1, text: 'Harry', children: [{ id: 2, text: 'David' }] }, { id: 3, text: 'Lisa' }, { id: 4, text: 'Mona' }, { id: 5, text: 'Ron' }, { id: 6, text: 'Joe' } ];
const toRemoveId = 2;
const filterItems = (items, fn) => items.reduce((acc, item) => {
if(item.children)
return [...acc, {...item, children: filterItems(item.children, fn)}];
else if(fn(item))
return [...acc, item];
return acc;
}, []);
const filtered = filterItems(items, item => item.id !== 2);
console.log(filtered);
This will work for arbitrary object depths.
I just wrote the filterById function I think it works for your case
var itemsTemp = [
{ id: 0, text: "Andy" },
{
id: 1,
text: "Harry",
children: [{ id: 2, text: "David" }],
},
{ id: 3, text: "Lisa" },
{ id: 4, text: "Mona" },
{ id: 5, text: "Ron" },
{ id: 6, text: "Joe" },
];
var items = itemsTemp;
const filterById = (items, id) => {
return items.reduce((accumulator, currentValue) => {
if(currentValue.children){
const newCurrentValue = filterById(currentValue.children, id)
currentValue = {...currentValue, children: newCurrentValue}
}
if(currentValue.id !== id){
return [...accumulator, currentValue]
}
return accumulator
},[])
}
console.log(filterById(itemsTemp,2));
console.log(itemsTemp)
I think you can do like this.
var itemsTemp= [
{ id: 0, text: 'Andy' },
{
id: 1, text: 'Harry',
children: [
{ id: 2, text: 'David' }
]
},
{ id: 3, text: 'Lisa' },
{ id: 4, text: 'Mona' },
{ id: 5, text: 'Ron' },
{ id: 6, text: 'Joe' }
];
var items = itemsTemp;
var filtered = items.filter(function(item) {
childrens=item.children;
if(childrens)
{
filteredchildren = childrens.filter(children=>children.id!==2);
item.children=filteredchildren;
}
return item.id !== 2;
});
console.log(filtered);
I have an object with a structure like below
const data = [
{ academicYearId: 1, classLevelId: 1, subjectId: 1, ...},
{ academicYearId: 1, classLevelId: 1, subjectId: 2, ...},
{ academicYearId: 1, classLevelId: 1, subjectId: 3, ...},
,,,
]
I need to create a function that will return unique columns e.g
const uniqueColumns = ( val, columns)=> {
//
}
const val = [
{ id: 1, name: 'n1', val: 1 },
{ id: 1, name: 'n1', val: 2 },
{ id: 2, name: 'n2', val: 1 },
{ id: 3, name: 'n2', val: 2 }
]
let result = uniqueColumns(val)
console.log(val)
/**
* Expected
* [{ id: 1, name: 'n1'}, { id: 2, name: 'n2'}, { id: 3, name: 'n2'}]
*/
}
I have tried to look at the various answers in the post How to get distinct values from an array of objects in JavaScript? and I have managed to come up with the below
const uniqueColumns = (val, columns) =>
([...new Set(
val.map(item =>
columns.reduce((prev, next) =>
({[next]: item[next], ...prev}), {})
).map(item => JSON.stringify(item)))
].map(item => JSON.parse(item)))
const val = [
{ id: 1, name: 'n1', val: 1 },
{ id: 1, name: 'n1', val: 2 },
{ id: 2, name: 'n2', val: 1 },
{ id: 3, name: 'n2', val: 2 }
]
const result = uniqueColumns(val, ['id', 'name'])
console.log(result)
What I was inquiring is if there is a better approach instead of having to Convert Object to string and back to object to achieve this
You can use array reduce method.
const val = [
{ id: 1, name: "n1", val: 1 },
{ id: 1, name: "n1", val: 2 },
{ id: 2, name: "n2", val: 1 },
{ id: 3, name: "n2", val: 2 },
];
const uniqueColumns = (val, columns) => {
let ret = val.reduce((p, c) => {
let obj = {};
columns.forEach((x) => (obj[x] = c[x]));
let key = Object.values(obj);
if (!p[key]) p[key] = obj;
return p;
}, {});
return Object.values(ret);
};
const result = uniqueColumns(val, ["id", "name"]);
console.log(result);
I have 2 arrays:
0: {id: 2, name: "TMA"}
1: {id: 3, name: "Hibbernate"}
0: {id: 1, name: "FB.DE"}
1: {id: 2, name: "TMA"}
2: {id: 3, name: "Hibbernate"}
3: {id: 4, name: "Event.it A"}
4: {id: 5, name: "Projket 2"}
5: {id: 6, name: "Projekt 1"}
I want to compare them and delete the objects with the id 2 and 3 cause both arrays have them and thats the similarity.
This is my Code so far:
const projectListOutput = projectsOfPersonArray.filter(project => data.includes(project));
console.log(projectListOutput);
But every time i run this projectListOutput is empty.
When using includes dont compare objects, Just build data as array of strings. Remaining code is similar to what you have.
arr1 = [
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
];
arr2 = [
{ id: 1, name: "FB.DE" },
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
{ id: 4, name: "Event.it A" },
{ id: 5, name: "Projket 2" },
{ id: 6, name: "Projekt 1" },
];
const data = arr1.map(({ id }) => id);
const result = arr2.filter(({ id }) => !data.includes(id));
console.log(result);
Your data array probably does not contain the exact same object references than projectsOfPersonArray. Look at the code below:
[{ foo: 'bar' }].includes({ foo: 'bar' });
// false
Objects look equal, but they don't share the same reference (= they're not the same).
It's safer to use includes with primitive values like numbers or strings. You can for example check the ids of your objects instead of the full objects.
You compare different objects, so every object is unique.
For filtering, you need to compare all properties or use a JSON string, if the order of properties is equal.
var exclude = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }],
data = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 1, name: "FB.DE" }, { id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 4, name: "Event.it A" }, { id: 5, name: "Projket 2" }, { id: 6, name: "Projekt 1" }],
result = data.filter(project =>
!exclude.some(item => JSON.stringify(item) === JSON.stringify(project))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do something similar to the next:
const source = [{
id: 1,
name: "FB.DE"
},
{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
},
{
id: 4,
name: "Event.it A"
},
{
id: 5,
name: "Projket 2"
},
{
id: 6,
name: "Projekt 1"
}
]
const toRemove = [{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
}
]
/**create object where keys is object "id" prop, and value is true**/
const toRemoveMap = toRemove.reduce((result, item) => ({
...result,
[item.id]: true
}), {})
const result = source.filter(item => !toRemoveMap[item.id])
You can make function from it:
function removeArrayDuplicates (sourceArray, duplicatesArray, accessor) {
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[item[accessor]]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[item[accessor]])
}
removeArrayDuplicates(source, toRemove, 'id')
Or even better, you can make it work with a function instead of just property accessor:
function removeDuplicates (sourceArray, duplicatesArray, accessor) {
let objectSerializer = obj => obj[accessor];
if(typeof accessor === 'function') {
objectSerializer = accessor;
}
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[objectSerializer(item)]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[objectSerializer(item)])
}
removeDuplicates(source, toRemove, (obj) => JSON.stringify(obj))
This function will help you merge two sorted arrays
var arr1 = [
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
]
var arr2 = [
{ id: 1, name: 'FB.DE' },
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
{ id: 4, name: 'Event.it A' },
{ id: 5, name: 'Projket 2' },
]
function mergeArray(array1, array2) {
var result = []
var firstArrayLen = array1.length
var secondArrayLen = array2.length
var i = 0 // index for first array
var j = 0 // index for second array
while (i < firstArrayLen || j < secondArrayLen) {
if (i === firstArrayLen) { // first array doesn't have any other members
while (j < secondArrayLen) { // we copy rest members of first array as a result
result.push(array2[j])
j++
}
} else if (j === secondArrayLen) { // second array doesn't have any other members
while (i < firstArrayLen) { // we copy the rest members of the first array to the result array
result.push(array1[i])
i++
}
} else if (array1[i].id < array2[j].id) {
result.push(array1[i])
i++
} else if (array1[i].id > array2[j].id) {
result.push(array2[j])
j++
} else {
result.push(array1[i])
i++
j++
}
}
return result
}
console.log(mergeArray(arr1,arr2));
Maybe someone can help here.
What I want to do is:
Build one result obj that consists of all the attributes / subobjects of all of these objects in the array
Always have only arrays in the result obj
Recursively build this object (as I don't know how many levels there could be and I don't know the names of the objects)
The order of the attributes is not relevant
Managable Except / black list for some attributes (such as id)
const arr = [{
id: 0,
nickname: 'Testnick 0',
loel: {
nice: 'like it',
},
rating: {
abc: 5,
helloworld: 2,
},
},
{
id: 1,
nickname: 'Testnick 2',
rating: {
abc: 4,
moep: 1,
},
},
{
id: 2,
nickname: 'Testnick 3',
rating: {
abc: 40,
sun: 20,
anotherObj: {
tr: 34,
subsubobj: {
sd: 24,
},
},
},
},
];
So the resultobj would look similar to this:
const result = {
id: [0, 1, 2],
nickname: ['Testnick 0', 'Testnick 2', 'Testnick 3'],
loel: {
nice: ['like it'],
},
rating: {
abc: [5, 4, 40],
helloworld: [2],
moep: [1],
sun: [20],
anotherObj: {
tr: [34],
subsubobj: {
sd: [24],
},
},
},
};
Can someone help here?
You coud reduce the given array and iterate the object's keys and values recursively for nested objects.
function setValues(target, source) {
Object.entries(source).forEach(([k, v]) => {
if (v && typeof v === 'object') {
setValues(target[k] = target[k] || {}, v);
} else {
target[k] = target[k] || [];
target[k].push(v);
}
});
return target;
}
var data = [{ id: 0, nickname: 'Testnick 0', loel: { nice: 'like it' }, rating: { abc: 5, helloworld: 2 } }, { id: 1, nickname: 'Testnick 2', rating: { abc: 4, moep: 1 } }, { id: 2, nickname: 'Testnick 3', rating: { abc: 40, sun: 20, anotherObj: { tr: 34, subsubobj: { sd: 24 } } } }],
result = data.reduce(setValues, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }