Compare two javascript array of objects and merge data - javascript

I'm having a bad time comparing two array of objects on a key.
I would like to compare, substract value when the key matches and display negative value when not in my target array. Finally, I want to have all target objects (if key didn't match) inside my final array.
An exemple would save 1000 words :
const initial = [{id: 1, value: 47}, {id: 2, value: 20}, {id: 7, value: 13}];
const target = [{id: 1, value: 150}, {id: 3, value: 70}, {id: 40, value: 477}];
//Desired output
// [{id: 1, value: 103}, {id: 2, value: -20}, {id: 7, value: -13}, {id: 3, value: 70}, {id: 40, value: 477}];
let comparator = [];
initial.map(initia => {
let hasSame = target.find(targ => {
return initia.id === targ.id
});
if(hasSame){
initia.value -= hasSame.value
} else{
initia.value = -initia.value
}
});
console.log(initial);
I'm getting almost the result I want except that I don't know how to merge target values properly. Is it possible to merge this values without looping over target array once more? Or could I do that inside the find ?
I want to get advice to do this as clean as possible
Thanks you!

You could use a Map and collect same id with a wanted factor for summing.
As result take key/value as new properties.
var add = (map, factor) => ({ id, value }) => map.set(id, map.has(id)
? map.get(id) - value * factor
: value * factor
),
initial = [ {id: 1, value: 47 }, { id: 2, value: 20 }, { id: 7, value: 13 }],
target = [{ id: 1, value: 150 }, { id: 3, value: 70 }, { id: 40, value: 477 }],
map = new Map,
result;
initial.forEach(add(map, -1));
target.forEach(add(map, 1));
result = Array.from(map, ([id, value]) => ({ id, value }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

If the intent is to avoid a nested find inside of the loop, you could reduce the two arrays in to a Set. They don't allow duplicate keys, so you would ensure a single value for each ID provided.
const valueSet = [...initial, ...target].reduce((total, obj) => {
total[obj.id] = !total[obj.id]
? -obj.value
: total[obj.id] -= obj.value
return total;
}, {});
const result = Object.keys(valueSet).map(key => ({ id: key, value: valueSet[key]}))
console.log(result);
Then you'd map back over the result to build out the intended array.

Related

Find the best match in an array

I have got the following array:
let x = [
{ name: "Bad", value: 2 },
{ name: "Critical", value: 1 },
{ name: "High", value: 5 },
{ name: "Medium", value: 5 },
];
The expectation is to look for "Critical" first, if the array has it, return that, else look for "High" then look for "Medium" and so on.
You can store the priorities in an array and then loop over the array and for every priority check if there's an object and whenever an object is found, return it. If the entire priorities array is exhausted then return null (or whatever you want).
const arr = [
{ name: "Bad", value: 2 },
{ name: "Critical", value: 1 },
{ name: "High", value: 5 },
{ name: "Medium", value: 5 },
],
priorities = ["Critical", "High", "Medium", "Bad"],
search = (arr, priorities) => {
for (let p of priorities) {
const obj = arr.find(({ name }) => name === p);
if (obj) {
return obj;
}
}
return null;
};
console.log(search(arr, priorities));
You can also sort the array based on the priority.
Create a Map that stores the priorities.
Sort arr based on the priorities stored in the map.
const arr = [
{ name: "Bad", value: 2 },
{ name: "Critical", value: 1 },
{ name: "High", value: 5 },
{ name: "Medium", value: 5 },
],
priorities = new Map([
["Critical", 4],
["High", 3],
["Medium", 2],
["Bad", 1],
]),
sortedArr = [...arr].sort(
(a, b) => priorities.get(b.name) - priorities.get(a.name)
);
console.log(sortedArr);
let value = x.find(x => x.name == "Critical")
|| x.find(x => x.name == "High")
|| x.find(x => x.name == "Medium") ...
First, define the order in which you want to look for items, then use that to sort the items in the array. Lastly, find based on all the items in your sort order. The first match will be returned, since the items are sorted.
const x = [{name: 'Bad',value: 2}, {name: 'High', value: 5}, {name: 'Medium', value: 5}, {name: 'Critical', value: 1}],
order = ['Critical','High','Medium'],
//sort according to above order
output = x.sort(
({name:na},{name:nb}) =>
order.findIndex(v => v === na) - order.findIndex(v => v === nb)
)
//Now find items matching name in order
.find( ({name}) => order.includes(name) );
console.log( output );
NOTE
Since your data is already sorted in the desired order, I moved the element with name = "Critical" to the end of the array.
If the data will always be sorted according to the priority you want to find the items, then no sorting is needed.

Transforming Javascript Array

I'm not sure what would be the best way to approach transforming this JS Array.
I have an array with a structure like (could contain more than 1 element):
let arr1 = [{amount: '10', quantity: 2, id: '123'}, {....}]
but I need to take that and transform that into an array structured like
let arr2 = [{value: '10'}, {value: '10'}]
Essentially adding new objects into the array based on the quantity in array 1.
I was thinking basic for loop but it seems to be getting a bit messy. Is there a simple way in JS to do this? Either with some sort of built in function etc?
You can easily get the result using flatMap.
First, you can create a temp array with the number of elements as quantity then map over the temp array to get the object with property amount in it.
let arr1 = [
{ amount: "10", quantity: 2, id: "123" },
{ amount: "30", quantity: 5, id: "123" },
];
const result = arr1.flatMap((obj) => {
const { amount, quantity } = obj;
return Array(quantity)
.fill(0)
.map((x) => ({ amount }));
});
console.log(result);
You can also make the above snippet succinct
const result = arr1.flatMap(({ quantity, amount }) => Array(quantity).fill(0).map((x) => ({ amount })));
let arr1 = [
{ amount: "10", quantity: 2, id: "123" },
{ amount: "30", quantity: 5, id: "123" },
];
const result = arr1.flatMap(({ quantity, amount }) =>
Array(quantity)
.fill(0)
.map((x) => ({ amount }))
);
console.log(result);
I was thinking basic for loop but it seems to be getting a bit messy
You just need a simple for and a while loop:
const input = [{amount: '10', quantity: 2, id: '123'}],
output = []
for (let { amount, quantity } of input)
while (quantity--)
output.push({ value: amount })
console.log(output)
You could create o.quantity number of objects using Array.from(). Use flatMap to get a flat array for each item in the input array
const output = input.flatMap(o =>
Array.from({ length: o.quantity }, _ => ({ value: o.amount }) )
)
You could also do it using reduce, with a manual for loop to append the correct number of entries.
let arr1 = [{amount: '10', quantity: 2, id: '123'}];
const result = arr1.reduce((accum, v) => {
for (let i = 0; i < v.quantity; i++) {
accum.push({value: v.amount});
}
return accum;
} , []);
This might have a slight performance improvement over flatMap, as it does not create any temporaries.

Javascript - Get unique and sorted array

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

Lodash. How to get aggregate array from array objects

For example, I have an array:
const reference = [{id: 1, value: 10}, {id: 2, value: 10}, {id: 3, value: 10}, {id: 4, value: 5}];
How to get an array values from reference like
const result = [0, 10, 20, 25];
First step always = 0
Second step 0 + 10 = 10
Third step 0 + 10 + 10 = 20
Forth step 0 + 10 + 10 + 5 = 25
You can reduce the array, and add the current value to the last sum:
const reference = [{id: 1, value: 10}, {id: 2, value: 10}, {id: 3, value: 10}, {id: 4, value: 5}];
const result = reference
.reduce((r, o, i) => {
r.push(i === 0 ? 0 : r[r.length - 1] + o.value);
return r;
}, [])
console.log(result);
You could map the values by taking a closure over the sum and take zero for the first element.
const
reference = [{ id: 1, value: 10 }, { id: 2, value: 10 }, { id: 3, value: 10 }, { id: 4, value: 5 }],
result = reference.map((sum => ({ value }, i) => sum += i && value)(0));
console.log(result);
The way I would do this would be by using the Array.reduce method as follows :
let result = [0]
reference.reduce((acc, cur) => {
result.push(Object.values(cur)[1]+result[result.length-1])
})
Hope it helps

JavaScript: Convert array of objects into hashmap

I have a set of values in an array where each value has an ID and LABEL.
Once I have the value array and type console value[0] and value[1], the output is:
value[0]
Object {ID: 0, LABEL: turbo}
value[1]
Object {ID: 1, LABEL: classic}
How can I store these values in a hash map like a key-value (ID-LABEL) pair, and store them in a json?
This could be achieved by calling reduce on your array of values (ie data), to obtain the required hash map (where ID is the key and value is the corresponding LABEL):
const data = [
{ID: 0, LABEL: 'turbo'},
{ID: 1, LABEL: 'classic'},
{ID: 7, LABEL: 'unknown'}
];
const hashMap = data.reduce((result, item) => {
return { ...result, [ item.ID ] : item.LABEL };
}, {});
const hashMapJson = JSON.stringify(hashMap);
console.log('hashMap', hashMap);
console.log('hashMapJson', hashMapJson);
/*
More concise syntax:
console.log(data.reduce((result, { ID, LABEL }) => ({ ...result, [ ID ] : LABEL }), {}))
*/
Try (where h={})
data.map(x=> h[x.ID]=x.LABEL );
const data = [
{ID: 0, LABEL: 'turbo'},
{ID: 1, LABEL: 'classic'},
{ID: 3, LABEL: 'abc'}
];
let h={}
data.map(x=> h[x.ID]=x.LABEL );
console.log(h);
You can iterator over each item in the array, and use the ID proeprty as a javascript objects key and the LABEL as the value.
var value = [{ID: 0, LABEL: "turbo"}, {ID: 1, LABEL: "classic"}];
let theNewMap = {};
for(var i = 0; i < value.length; i++) {
theNewMap[value[i].ID] = value[i].LABEL;
}
// theNewMap Should now be a map with 'id' as key, and 'label' as value
console.log(JSON.stringify(theNewMap ))
You can use forEach method.
> var hmap = {};
undefined
> var value = [{ID: 0, LABEL: "turbo"}, {ID: 1, LABEL: "classic"}]
undefined
> value.forEach(function(element){
... hmap[element.ID] = element.LABEL;
... });
> hmap
{ '0': 'turbo', '1': 'classic' }
or
var value = [{ID: 0, LABEL: "turbo"}, {ID: 1, LABEL: "classic"}]
var hmap = {};
value.forEach(function(element){
hmap[element.ID] = element.LABEL;
});
console.log(hmap);

Categories