Javascript - Detect reverse pairs in an array of objects - javascript

I have an array of objects:
[
{source: 'A', target : 'C', value: 25},
{source: 'E', target: 'F', value: 5},
{source: 'C', target: 'A', value: 20}, ...
]
How can I detect all reverse pairs such as source: 'A', target: 'C' and source: 'C', target: 'A' and make a sum of value: 25 + 20 ?

First generate all possible pairs using two for loops, then iterate over each pari using forEach to check if the source/target properties match, if so, compute the sum:
const arr = [
{source: 'A', target : 'C', value: 25},
{source: 'E', target: 'F', value: 5},
{source: 'C', target: 'A', value: 20}
];
const pairs = arr => {
const out = [];
for (let i = 0; i < arr.length - 1; ++i) {
for (let j = i; j < arr.length - 1; ++j) {
out.push([arr[i], arr[j + 1]]);
}
}
return out;
}
pairs(arr).forEach(pair => {
if (pair[0].source === pair[1].target && pair[0].target === pair[1].source) {
console.log(pair[0].value + pair[1].value);
}
});

You could use a sorted joint key for a pair with a hash table.
var array = [{ source: 'A', target : 'C', value: 25 }, { source: 'E', target: 'F', value: 5}, { source: 'C', target: 'A', value: 20 }],
hash = {},
pairs = array.reduce((r, { source, target, value }) => {
var key = [source, target].sort().join('|');
if (!hash[key]) {
r.push(hash[key] = { source, target, value });
return r;
}
hash[key].value += value;
return r;
}, []);
console.log(pairs);

Try something like this
var abc = [
{source: 'A', target : 'C', value: 25},
{source: 'E', target: 'F', value: 5},
{source: 'C', target: 'A', value: 20},
{source: 'C', target: 'A', value: 200} //To test all pairs
]
//Here you can use for loop to get pairs of each record
var sourceA = abc[0].source;
var targetA = abc[0].target;
//check source against target and vice versa
var result = abc.filter(x => x.target == sourceA && x.source == targetA);
console.log("Result");
console.log(result);
//if you want sum then
var totalAmount = 0;
result.forEach( data => totalAmount = totalAmount + data.value);
console.log("Total amount");
console.log(totalAmount);

When you're looking for all the pairs, you can use:
var arr = [
{source: 'A', target : 'C', value: 25},
{source: 'E', target: 'F', value: 5},
{source: 'C', target: 'A', value: 20},
{source: 'D', target: 'A', value: 2},
{source: 'A', target: 'D', value: 1}
]
var res = arr.reduce((acc, {source,target,value}, id, input) => {
var findPair = el => el.source == target && el.target == source;
var pair = !acc.some(findPair) && input.find(findPair);
return pair ? [...acc, {source, target, sum: pair.value + value}]: acc;
}, []);
This returns:
[ { source: 'A', target: 'C', sum: 45 },
{ source: 'D', target: 'A', sum: 3 } ]

Related

how do I replace values of specific keys in an array of objects? javascript

I have an array of objects where I need to modify and replace values of specific keys:
arr = [ {key: 'a', value: 1}, {key: 'b', value: 2}, {key: 'c', value: 3}, {key: 'd', value: 4}, {key: 'e', value: 5}]
furthest I could figure was to filter arr.filter(i => i.key.includes('b','c','d')).map(i => i.value) but this would stop only on the first included filter and hold only one value (2). Id thought it would hold objects of keys b c d which values id then modify.
const arr = [{
key: 'a',
value: 1
}, {
key: 'b',
value: 2
}, {
key: 'c',
value: 3
}, {
key: 'd',
value: 4
}, {
key: 'e',
value: 5
}]
console.log(arr.filter(i => i.key.includes('b', 'c', 'd')).map(i => i.value))
Just iterate array and check if key of item matches desired keys and if so assign new value.
Because arr is an array of objects and objects are stored by reference you can alter the object directly without creating a new one.
const keys = ["a", "b", "c"];
const newValue = 1;
arr.forEach((item) => {
if (keys.includes(item.key)) {
item.value = newValue;
}
})

How to compare object based on key and value and remove from array

I have below data in const rows.
const rows = {
"selected_parameter_value": [
{
"parameter_value": "a",
"label_value": "a"
},
{
"parameter_value": "d",
"label_value": "d"
}
]
};
I want to compare each and every object of
const rows.selected_parameter_value with parameter_value
and label_value from this.selectedParameterContext.records[0].selected_parameter_value.
And delete those objects from this.selectedParameterContext.records[0].selected_parameter_value which are not present in const rows.selected_parameter_value
For example -
In the const rows.selected_parameter_value, only a and d object are present, b and c are not present.
So remove object whose parameter and label values are b and c from this.selectedParameterContext.records[0].selected_parameter_value.
this.selectedParameterContext = {
'records': [
{
'selected_parameter_value': [{
'parameter_value': 'a',
'label_value': 'a'
},
{
'parameter_value': 'b',
'label_value': 'b',
}]
},
{
'selected_parameter_value': [{
'parameter_value': 'c',
'label_value': 'c'
},
{
'parameter_value': 'd',
'label_value': 'd',
}]
}]
};
**Expected Output -**
this.selectedParameterContext = {
'records': [
{
'selected_parameter_value': [{
'parameter_value': 'a',
'label_value': 'a'
}]
},
{
'selected_parameter_value': [
{
'parameter_value': 'd',
'label_value': 'd',
}]
}]
};
I tried below code
deleteContextData(rows ) {
const paramArray = rows.selected_parameter_value;
const newArrayData = this.selectedParameterContext.records[0].selected_parameter_value;
const removeMatchingData = (paramArray, toCompareWith) => {
return paramArray.filter(({ label_value }) => !toCompareWith.some(compareObj => compareObj.label_value === label_value));
}
console.log(removeMatchingData(paramArray, newArrayData),"newarray");
}
You can filter the data/source using Array.filter by comparing each object by matching object with the same key-value present in the comparing/target array and return only the ones which are not matching in the source array.
let data = [{label_value: 'a', parameter_value: 'a'}, {label_value: 'b', parameter_value: 'b'}, {label_value: 'c', parameter_value: 'c'}, {label_value: 'd', parameter_value: 'd'}]
let contextData = [{label_value: 'b', parameter_value: 'b'}, {label_value: 'c', parameter_value: 'c'}];
const removeMatchingData = (data, toCompareWith) => {
return data.filter(({ label_value }) => !toCompareWith.some(compareObj => compareObj.label_value === label_value));
}
console.log(removeMatchingData(data, contextData));
contextData = [{label_value: 'b', parameter_value: 'b'}, {label_value: 'c', parameter_value: 'c'}, {label_value: 'e', parameter_value: 'e'}];
console.log(removeMatchingData(contextData, data));

remove common elements between multiple arrays

I have 3 arrays (or more/less, it's not mandatory to be 3, I just gave an example) and I want to remove all the common elements between them. For example, between the first 2, the common elements are x and z, between the second and the third array the common element would be t. Between the first and the thirs the common element is k. Basically I want to remove any elements that appear more than 1 times in multiple arrays.
!! the first array can have common elements with the third one !!
Here is what I tried so far, but it's not working correctly.
let y = [{
id: 'a',
elems: ['x', 'y', 'z', 'k']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'k', 'o', 't']
},
]
// x, z, t
for (let i = 0; i < y.length - 1; i++) {
let current = y[i].elems
let current2 = y[i + 1].elems
if (current[i] == current2[i]) {
const index = current.indexOf(current[i]);
if (index > -1) {
current.splice(index, 1);
current2.splice(index, 1);
}
}
}
console.log(y)
The desired result would be
[
{
"id": "a",
"elems": [
"y"
]
},
{
"id": "b",
"elems": [
"u",
"i"
]
},
{
"id": "c",
"elems": [
"m",
"n",
"o"
]
}
]
Which would be a correct and optimal solution for this? I also tried to concatenate the 3 arrays and remove the duplicates, but then I don't know how to recreate the 3 arrays back.. Thanks!
I would first loop over all the elems and count up how many times that have been seen. After that I would loop again and filter out anything that was seen more than once.
const myData = [{
id: 'a',
elems: ['x', 'y', 'z']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'o', 't']
},
]
// count up every elem so we know which ones are duplicated
const allElems = myData.reduce((acc, item) => {
item.elems.forEach( key => {
acc[key] = acc[key] || 0;
acc[key]++;
});
return acc;
}, {})
// loop over all the elems and select only the elems that we have seen once
myData.forEach(item => {
item.elems = item.elems.filter(key => allElems[key] === 1);
})
console.log(myData)
let x = ['a', 'b']
let y = [{
id: 'a',
elems: ['x', 'y', 'z', 'k']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'k', 'o', 't']
},
]
// x, z, t
for (let i = 0; i < y.length - 1; i++) {
for (let j = 1; j < y.length; j++) {
let current = y[i].elems
let current2 = y[j].elems
current2.forEach((item,index)=>{
if(current.includes(item)){
current.splice(current.indexOf(item),1)
current2.splice(index,1)
}
})
}
}
console.log(y)
.as-console-wrapper { max-height: 100% !important; top: 0; }
const y = [
{ id: 'a', elems: ['x', 'y', 'z'] },
{ id: 'b', elems: ['x', 't', 'u', 'i', 'z'] },
{ id: 'c', elems: ['m', 'n', 'o', 't'] },
];
// get number of occurences for each elem
const elems
= y.flatMap(e => e.elems).reduce((acc,elem) => {
acc[elem] = acc[elem] ? acc[elem]+1 : 1;
return acc;
}, {});
// get unique elems
const unique = Object.keys(elems).filter(elem => elems[elem]===1);
// remove non-unique elems from each item
const res = y.map(item =>
({ ...item, elems: item.elems.filter(e => unique.includes(e)) })
);
console.log(res);
Using a Map to track counts after one loop through then use the count of that Map in a filter for final results
let x = ['a', 'b']
let y = [{
id: 'a',
elems: ['x', 'y', 'z']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'o', 't']
},
]
const counts = new Map()
// first iteration to count values
y.forEach(({ elems }) => elems.forEach(v => counts.set(v, (counts.get(v) || 0) + 1)));
// second iteration to filter out dups
y.forEach(e => e.elems = e.elems.filter(v => counts.get(v) === 1))
console.log(y)
Let me know if this works for you.
let y = [
{
id: "a",
elems: ["x", "y", "z", "k"],
},
{
id: "b",
elems: ["x", "t", "u", "i", "z"],
},
{
id: "c",
elems: ["m", "n", "x", "z", "t"],
},
];
// For every element in first array
for (let el of y[0].elems) {
//If we find that every other array includes it
if (y.every((obj) => obj.elems.includes(el))) {
//Remove it from all arrays
for (let obj of y) {
obj.elems = obj.elems.filter((x) => x !== el);
}
}
}
let y = [{
id: 'a',
elems: ['x', 'y', 'z']
},
{
id: 'b',
elems: ['x', 't', 'u', 'i', 'z']
},
{
id: 'c',
elems: ['m', 'n', 'o', 't']
},
];
//
const notExist = (x, arr) => !arr.find(el => el == x);
const restToArrays = (i, arr) => arr.reduce((a, b, index) => index == i ? a : [...a, ...b.elems], []);
const result = y.map((ligne, index, arr) => ({
id: ligne.id,
elems: ligne.elems.filter(v => notExist(v, restToArrays(index, arr)))
}))
console.log(result);

Sort an array by its relative position

Example object array:
[{
id: 'a',
beforeId: null
}, {
id: 'b',
beforeId: 'c'
}, {
id: 'c',
beforeId: 'a'
}, {
id: 'd',
beforeId: 'b'
}]
Output order: d-b-c-a; each element sorted relative to each other element based on its beforeId property.
I could make a temporary array and sort the above array. Is sorting possible with array.sort?
You could build an object with the relations and generate the result by using the object with beforeId: null and unshift all objects for the result array.
The next object is the one with the actual val as key.
Complexity: O(2n).
function chain(array) {
var o = {}, pointer = null, result = [];
array.forEach(a => o[a.beforeId] = a);
while (o[pointer]) {
result.unshift(o[pointer]);
pointer = o[pointer].val;
}
return result;
}
var data = [{ val: 'a', beforeId: null }, { val: 'b', beforeId: 'c' }, { val: 'c', beforeId: 'a' }, { val: 'd', beforeId: 'b' }];
console.log(chain(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is a terribly inefficient and naïve algorithm, but it works:
const array = [
{id: 'a', beforeId: null},
{id: 'b', beforeId: 'c'},
{id: 'c', beforeId: 'a'},
{id: 'd', beforeId: 'b'}
];
// find the last element
const result = [array.find(i => i.beforeId === null)];
while (result.length < array.length) {
// find the element before the first element and prepend it
result.unshift(array.find(i => i.beforeId == result[0].id));
}
console.log(result);
Is sorting possible with array.sort?
sure, with a helper function:
graph = [
{id: 'a', beforeId: null},
{id: 'b', beforeId: 'c'},
{id: 'c', beforeId: 'a'},
{id: 'd', beforeId: 'b'}
];
let isBefore = (x, y) => {
for (let {id, beforeId} of graph) {
if (id === x)
return (beforeId === y) || isBefore(beforeId, y);
}
return false;
};
graph.sort((x, y) => x === y ? 0 : (isBefore(x.id, y.id) ? -1 : +1))
console.log(graph);
isBefore returns true if x is before y immediately or transitively.
For generic, non-linear topological sorting see https://en.wikipedia.org/wiki/Topological_sorting#Algorithms
UPD: As seen here, this turned out to be horribly inefficient, because sort involves many unnecessary comparisons. Here's the fastest (so far) version:
function sort(array) {
let o = {}, res = [], len = array.length;
for (let i = 0; i < len; i++)
o[array[i].beforeId] = array[i];
for (let i = len - 1, p = null; i >= 0; i--) {
res[i] = o[p];
p = o[p].id;
}
return res;
}
which is the #Nina's idea, optimized for speed.
You may try this approach :
// order(null, vals, []) = ["d", "b", "c", "a"]
function order(beforeId, vals, result){
var id = beforeId || null;
var before = vals.filter(function(val){
return val.beforeId === id
});
if (before.length === 0) return result;
return order(before[0].val,
vals,
[before[0].val].concat(result));
}

Convert Array To a Specific Object

What is the best way to convert this array :
array=['a', 'b', 'c', 'd']
to
$scope.editcity = {
cities : [
{id: 1, name: "a", selected: false},
{id: 2, name: "b", selected: false},
{id: 3, name: "c", selected: false},
{id: 4, name: "d", selected: false}
]}
With a map
var array=['a', 'b', 'c', 'd']
var $scope = {} // just for this test - you wont need this line
$scope.editcity = {
cities : array.map(function(c,i){
return {
id: i+1,
name: c,
selected:false
}
})
};
console.log($scope.editcity)
Use Array#map method.
var array = ['a', 'b', 'c', 'd']
$scope.editcity = {
cities : array.map(function(v, i){
return {
id : i + 1,
name : v,
selected : false
}
})
}
var array = ['a', 'b', 'c', 'd']
var editcity = {
cities: array.map(function(v, i) {
return {
id: i + 1,
name: v,
selected: false
}
})
}
console.log(editcity);
You can use reduce method to return object.
var array=['a', 'b', 'c', 'd']
var editcity = array.reduce(function(r, e, i) {
r.city = (r.city || []).concat({id: i + 1, name: e, selected: false})
return r
}, {})
console.log(editcity)

Categories

Resources