Sort JavaScript Array in Artificial Order - javascript

I have an array returned by a REST query that represents the number of items in a multi-step production process.
var steps = [
{ name:'Package', value:3},
{ name:'Assemble', value:1 },
{ name:'Ship', value:7},
{ name:'Preprocess', value:9 },
{ name:'Paint', value:5 }
];
I'd like to sort them in the order of the process, like this:
Preprocess
Paint
Assemble
Package
Ship
I have other alphanumeric sorts that I am doing with Underscore but I cannot figure this one out.

You could take an object for the wanted order with numerical values for the position. Then sort by this values.
var steps = [{ name: 'Package', value: 3 }, { name: 'Assemble', value: 1 }, { name: 'Ship', value: 7 }, { name: 'Preprocess', value: 9 }, { name: 'Paint', value: 5 }],
order = { Preprocess: 1, Paint: 2, Assemble: 3, Package: 4, Ship: 5 };
steps.sort(({ name: a }, { name: b }) => order[a] - order[b]);
console.log(steps);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can create an object which mentions the priority on each name while ordering.
let priority = {
Preprocess: 1,
Paint: 2,
Assemble: 3,
Package: 4,
Ship: 5,
}
var steps = [
{ name:'Package', value:3},
{ name:'Assemble', value:1 },
{ name:'Ship', value:7},
{ name:'Preprocess', value:9 },
{ name:'Paint', value:5 }
];
const result = steps.sort((a,b) => priority[a.name] - priority[b.name]);
console.log(result);

I'm just using a precedence map for every step type so that I can use it in the compare function.
var steps = [
{ name: 'Package', value: 3 },
{ name: 'Assemble', value: 1 },
{ name: 'Ship', value: 7 },
{ name: 'Preprocess', value: 9 },
{ name: 'Paint', value: 5 }
];
var stepPrecedenceMap = new Map([["Preprocess", 1], ["Paint", 2], ["Assemble", 3], ["Package", 4], ["Ship", 5]])
console.log(
steps.sort((stepA, stepB) => {
return stepPrecedenceMap.get(stepA.name) - stepPrecedenceMap.get(stepB.name)
}));

Related

How to sort array with selected list in javascript?

I have a list look like:
const initArray = [
{
id: 0,
},
{
id: 1,
},
{
id: 2,
},
{
id: 3,
},
];
A selected list look like:
const selectedList = [
{
id: 2,
},
];
And the desired data has been sorted:
const outPut= [
{
id: 2,
},
{
id: 0,
},
{
id: 1,
},
{
id: 3,
},
];
I'm in trouble right now, so I can't figure it out yet.
Can you share some solutions?
You could take an object which keeps the order of the first objects and sort the rest after.
const
data = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }],
selectedList = [{ id: 2 }],
order = Object.fromEntries(selectedList.map(({ id }, i) => [id, i + 1]));
data.sort((a, b) => (order[a.id] || Number.MAX_VALUE) - (order[b.id] || Number.MAX_VALUE));
console.log(data);
Using Set and Array#map, get set of ids to prioritize
Using Array#sort, sort the items using the above set
const _sort = (arr = [], selected = []) => {
const priority = new Set( selected.map(({ id }) => id) );
return [...arr].sort(({ id: a }, { id: b }) => priority.has(b) - priority.has(a));
}
const
initArray = [ { id: 0 }, { id: 1 }, { id: 2 }, { id: 3 } ],
selectedList = [ { id: 2 } ];
console.log( _sort(initArray, selectedList) );

How to create new array base on JSON value

i have question about array, for example i have an array of object, like this
const data = [
{
level: 1,
name: "Naruto",
},
{
level: 2,
name: "Dragon",
},
{
level: 2,
name: "Ball",
},
{
level: 3,
name: "Sasuke",
},
]
Now i want to create new array base on level , mean that after format it will look like this:
[
[
{
name: "naruto"
}
],
[
{
name: "Ball"
},
{ name: "Dragon" }
],
[
{name:"Sasuke"}
]
];
How can i do this, thanks you guys
I used a loop to handle your case
currentLevel - 1 is mapped with the index of the result's array
const data = [{
level: 1,
name: "Naruto",
},
{
level: 2,
name: "Dragon",
},
{
level: 2,
name: "Ball",
},
{
level: 3,
name: "Sasuke",
},
];
let result = []
for (const currentValue of data) {
const currentLevel = currentValue.level
if (!result[currentLevel - 1]) {
result[currentLevel - 1] = []
}
result[currentLevel - 1].push({
name: currentValue.name
})
}
//it's followed by index, the result will have some `undefined` if your levels have gaps. You can comment it out, if you want to keep `undefined` values
result = result.filter(item => item)
console.log({
result
})

How to groupBy the array of object and concat into single array? [duplicate]

This question already has answers here:
group array of objects by id
(8 answers)
Closed 1 year ago.
I want to group the array of objects based on the key and concat all the grouped objects into a single array. GroupBy based on the id
example,
payload
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
expected response
[
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
}
],
[
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
]
All the matched elements are in the same array and all the arrays should be in a single array.
Array.redue will help
const input = [
{ id: 1, name: 'a' },
{ id: 1, name: 'b' },
{ id: 1, name: 'c' },
{ id: 2, name: 'b' },
{ id: 2, name: 'c' }
];
const output = input.reduce((acc, curr) => {
const node = acc.find(item => item.find(x => x.id === curr.id));
node ? node.push(curr) : acc.push([curr]);
return acc;
}, []);
console.log(output)
Extract the ids using Set so you have a unique set of them,
then loop over those ids and filter the original array based on it.
let objects = [
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
let ids = [...new Set(objects.map(i => i.id))]
let result = ids.map(id => objects.filter(n => id === n.id))
console.log(result)
you can create a object with ids array by using Array.reduce method, and get the object values by Object.values
var s = [{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
];
//go through the input array and create a object with id's, group the values to gather
var ids = s.reduce((a, c) => {
//check object has the `id` property, if not create a property and assign empty array
if (!a[c.id])
a[c.id] = [];
//push the value into desidred object property
a[c.id].push(c)
//return the accumulator
return a;
}, {});
//get the grouped array as values
var outPut = Object.values(ids);
console.log(outPut);
1) You can easily achieve the result using Map and forEach easily
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const map = new Map();
arr.forEach((o) => !map.has(o.id) ? map.set(o.id, [o]) : map.get(o.id).push(o));
const result = [...map.values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
2) You can also achieve the result using reduce
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const result = [...arr.reduce((map, curr) => {
!map.has(curr.id) ? map.set(curr.id, [curr]) : map.get(curr.id).push(curr);
return map;
}, new Map()).values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }

compare two arrays in javascript and delete the object that both arrays have

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));

Merge Array of Objects by Property using Lodash

I have two arrays of objects that represent email addresses that have a label and a value:
var original = [
{
label: 'private',
value: 'private#johndoe.com'
},
{
label: 'work',
value: 'work#johndoe.com'
}
];
var update = [
{
label: 'private',
value: 'me#johndoe.com'
},
{
label: 'school',
value: 'schhol#johndoe.com'
}
];
Now I want to compare and merge the two arrays by the label field, so that the result would look like this:
var result = [
{
label: 'private',
value: 'me#johndoe.com'
},
{
label: 'work',
value: 'work#johndoe.com'
},
{
label: 'school',
value: 'schol#johndoe.com'
}
]
How can I do this e.g. using lodash?
_.unionBy():
This method is like _.union except that it accepts iteratee which is invoked for each element of each arrays to generate the criterion by which uniqueness is computed. Result values are chosen from the first array in which the value occurs.
var original = [
{ label: 'private', value: 'private#johndoe.com' },
{ label: 'work', value: 'work#johndoe.com' }
];
var update = [
{ label: 'private', value: 'me#johndoe.com' },
{ label: 'school', value: 'schol#johndoe.com' }
];
var result = _.unionBy(update, original, "label");
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Convert the lists to objects keyed by label, merge them by _.assign, and convert it back to an array. It will even retain order of the items on most browsers.
var original = [
{
label: 'private',
value: 'private#johndoe.com'
},
{
label: 'work',
value: 'work#johndoe.com'
}
];
var update = [
{
label: 'private',
value: 'me#johndoe.com'
},
{
label: 'school',
value: 'schol#johndoe.com'
}
];
console.log(
_.map(
_.assign(
_.mapKeys(original, v => v.label),
_.mapKeys(update, v => v.label)
)
)
);
// or remove more duplicated code using spread
console.log(
_.map(
_.assign(
...[original, update].map(
coll => _.mapKeys(coll, v => v.label)
)
)
)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.js"></script>
Perhaps a bit late, but all the solutions I have seen don't join both arrays correctly, they use one of the arrays to loop on and any excess elements in the second array don't get added (assuming this is what is required).
The right way is to sort both arrays and move forward within both arrays, merging the matches elements and adding the missing elements from both arrays.
Please find full solution below. This also takes O(n+m) which is the best you can get (without the computational costs for sort itself). In my code I already got the data sorted from the database.
function mergeObjectsBasedOnKey(array1, array2, compareFn, mergeFn, alreadySorted) {
var array1Index = 0;
var array2Index = 0;
const merged = [];
if (!alreadySorted) {
array1.sort(compareFn);
array2.sort(compareFn);
}
while (array1Index < array1.length && array2Index < array2.length) {
var comparedValue = compareFn(array1[array1Index], array2[array2Index]);
if (comparedValue === 0) {
merged.push(mergeFn(array1[array1Index], array2[array2Index]));
array1Index++;
array2Index++;
} else if (comparedValue < 0) {
merged.push(mergeFn(array1[array1Index]));
array1Index++;
} else {
merged.push(mergeFn(array2[array2Index]));
array2Index++;
}
}
while (array1Index < array1.length) {
merged.push(mergeFn(array1[array1Index]));
array1Index++;
}
while (array2Index < array2.length) {
merged.push(mergeFn(array2[array2Index]));
array2Index++;
}
return merged;
}
const array1 = [{
"id": 10,
isArray1: true
},
{
"id": 11,
isArray1: true
},
{
"id": 12,
isArray1: true
},
];
const array2 = [{
"id": 8,
isArray2: true
},
{
"id": 11,
isArray2: true
},
{
"id": 15,
isArray2: true
},
];
const result = mergeObjectsBasedOnKey(array1, array2, function(a, b) {
return a.id - b.id;
}, function(a, b) {
if (b) {
return _.merge(a, b);
}
return _.merge(a, {
isArray1: true,
isArray2: true
});
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
And the results would be:
[ { id: 8, isArray2: true, isArray1: true },
{ id: 10, isArray1: true, isArray2: true },
{ id: 11, isArray1: true, isArray2: true },
{ id: 12, isArray1: true, isArray2: true },
{ id: 15, isArray2: true, isArray1: true } ]
In case you are using lodash 3.x where _.unionBy() was not there, you can combine _.union() and _.uniq() to get the same result.
var original = [
{ label: 'private', value: 'private#johndoe.com' },
{ label: 'work', value: 'work#johndoe.com' }
];
var update = [
{ label: 'private', value: 'me#johndoe.com' },
{ label: 'school', value: 'schol#johndoe.com' }
];
var result = _.uniq(_.union(update, original), "label");
console.log(result);
I know it is not what asked for but just in case someone stumbled up on this page here is how you do this in ramda:
var original = [
{ label: 'private', value: 'private#johndoe.com' },
{ label: 'work', value: 'work#johndoe.com' }
];
var updated = [
{ label: 'private', value: 'me#johndoe.com' },
{ label: 'school', value: 'schol#johndoe.com' }
];
unionWith(eqBy(prop('label')), updated, original);
Here is another way to merge two objects using Lodash:
let a = [{
content: 'aaa',
name: 'bbb2'
},
{
content: 'aad',
name: 'ccd'
}
];
let b = [{
content: 'aaa',
name: 'bbb'
},
{
content: 'aad1',
name: 'ccd1'
}
];
let c = [...a, ...b];
let d = _.uniq(c, function(data) {
return data.content;
})
console.log(d);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
Perhaps a bit late, but all the solutions I have seen don't join both arrays correctly, they use one of the arrays to loop on and any excess elements in the second array don't get added (assuming this is what is required).
I had the same observation so put something together myself. This is working for my use case, which is to merge each object if the value of the 'label' field matches:
const dataSetHashes = dataSets.map(dataSet => _.keyBy(dataSet, 'label'))
const resultHash = _.merge(
{},
...dataSetLookups
)
const result = Object.values(resultLookup)

Categories