relative complement of A in B with functional programming - javascript

I have to retrieve the values that exist only on Array B, but do not exist on Array A.
From my research, It is called:
relative complement of A in B
Values in the arrays may not be primitives.I need an efficient and functional apporach to this problem.
I have found lodash _.without function, but it supports only array of primitive numbers.
Array A:
[{
id: 1
},
{
id:2
}]
Array B:
[{
id:2
},
{
id:3
}]
result should be:
[{
id:3
}]
this object is the only one who exist on Array B, but not on Array A.

You could use a comparison function which takes two objects and check the id for unequalness.
var aa = [{ id: 1 }, { id: 2 }],
bb = [{ id: 2 }, { id: 3 }],
comparison = (a, b) => a.id !== b.id,
result = bb.filter(b => aa.every(a => comparison(a, b)));
console.log(result);
With a check for equalness
var aa = [{ id: 1 }, { id: 2 }],
bb = [{ id: 2 }, { id: 3 }],
comparison = (a, b) => a.id === b.id,
result = bb.filter(b => aa.every(a => !comparison(a, b)));
console.log(result);

You can use array#filter with array#some. Iterate through arrB and check if the arrA contains that id using array#some and negate the result of array#some.
var arrA = [{id: 1},{id:2}],
arrB = [{id:2},{id:3}],
result = arrB.filter(({id}) => !arrA.some(o => o.id === id));
console.log(result);

You can use array.prototype.filter and array.prototype.findIndex:
var arrayA = [{ id: 1 }, { id: 2 }];
var arrayB = [{ id: 2 }, { id: 3 }];
var result = arrayB.filter(b => arrayA.findIndex(a => a.id === b.id) === -1);
console.log(result);

If you want to use lodash, _.differenceBy could be of use:
relativeComplementOfAinB = _.differenceBy(arrayB, arrayA, v => v.id);

Related

Adding Ids to an Array

I tried adding an id property to the objects in my sorted output, but all I'm doing is not working. Is there anything I should have done?
My Code Below:
var arr = [{ one: 2 },
{ two: 3 },
{ three: 4 },
{ four: 1 }];
const arr1 = arr.reduce((a,b) => ({...a,...b}), {})
var sorting = Object.entries(arr1).sort((a, b) => b[1] - a[1]);
console.log(sorting);
Expected Result:
var arr1 = [{ name: "three", value: 4, id: 1 },
{ name: "two", value: 3, id: 2 },
{ name: "one", value: 2, id: 3 },
{ name: "four", value: 1, id: 4 }];
Object.assign can do what you did with reduce, and I would not call that result arr1, as it is not an array, but a plain object.
In the final step it helps to use destructuring and map to an object literal with shortcut notation:
const arr = [{one: 2}, {two: 3}, {three: 4}, {four: 1}];
const obj = Object.assign({}, ...arr);
const sorted = Object.entries(obj).sort((a, b) => b[1] - a[1]);
const result = sorted.map(([text, value], i) => ({ text, value, id: i+1}));
console.log(result);
/*If i console.log(sorting) I have
[['three', 4 ], ['two', 3 ], ['one', 2 ], ['four', 1 ],]
Without Ids but i want something like the expected result below*/
/* Expected Result
[['three', 4 id = 1], ['two', 3 id = 2], ['one', 2 id = 3], ['four', 1 id = 4],]
*/
UPD, sorry, didn't get it right first time
var empty = [];
var arr = [{
one: 2
}, {
two: 3
}, {
three: 4
},{
four: 1
}];
const arr1 = arr.reduce((a,b) => ({...a,...b}), {})
const sorting = Object.entries(arr1).sort((a, b) => b[1] - a[1]);
// Add indexes starting from 1
const indexed = sorting.map((a,b) => a.push({ "id": b+1 }));
console.log(sorting);
I am going to go with a guess here that you want a descending sorted array of objects, adding an id property based on the original index + 1 of each original object. We can do that by reference to the object key (first property 0) when we sort after we add the ids to the original objects in a new array.
// not used in the question/issue
//var empty = [];
var arr = [{
one: 2
}, {
two: 3
}, {
three: 4
}, {
four: 1
}];
const newArr = [];
arr.forEach(function(currentValue, index, arr) {
currentValue.id = index + 1;
newArr.push(currentValue);
}, arr);
//console.log("arrObj:", newArr);
var sorted = newArr.sort(function(a, b) {
return b[Object.keys(b)[0]] - a[Object.keys(a)[0]];
});
console.log("sorted:", sorted);
EDIT: new based on comment
var arr = [{
one: 2
}, {
two: 3
}, {
three: 4
}, {
four: 1
}];
const newArr = [];
arr.forEach(function(currentValue, index, arr) {
let newValue = {};
newValue.text = Object.keys(currentValue)[0];
newValue.Value = currentValue[Object.keys(currentValue)[0]];
newValue.id = index + 1;
newArr.push(newValue);
}, arr);
//console.log("arrObj:", newArr);
var sorted = newArr.sort(function(a, b) {
return b.Value - a.Value;
});
console.log("sorted:", sorted);
output of this last is
sorted: [
{
"text": "three",
"Value": 4,
"id": 3
},
{
"text": "two",
"Value": 3,
"id": 2
},
{
"text": "one",
"Value": 2,
"id": 1
},
{
"text": "four",
"Value": 1,
"id": 4
}
]

How to merge two object TypeScript?

I have two objects:
let a = [{id: 1, selected: false, key: "plan"}];
let b = [{id: 1, selected: true, key: "plan", "text": "aaaa"}, {id: 2, selected: true}];
I need to merge them and get:
let c = [{id: 1, selected: true, key: "plan", "text": "aaaa"}, {id: 2, selected: true}];
My main purpose to rewrite default object on modified
I have tried:
let c = {...a, ...b};
You can use reduce in order to replace the data that is being fetched from server.
Below I have simulated different scenarios considering a as the original array and b & c as the responses from the server
let a = [{ id: 1, selected: false, key: 'plan' }];
let b = [
{ id: 1, selected: true, key: 'plan', text: 'aaaa' },
{ id: 2, selected: true },
];
const mergeArrays = (array1, array2) => {
array2 = array2 || [];
array1 = array1 || [];
return Object.values([...array1, ...array2].reduce((result, obj) => {
result = {
...result,
[obj.id]: {...obj}
}
return result;
}, {}))
};
//Consider you got the response from server
//a -> Original array, b -> Response from serer
console.log(mergeArrays(a, b))
//Response from server is null or []
console.log(mergeArrays(a, null))
//original array is empty but there's a response from server
console.log(mergeArrays([], b))
//Consider there is completely a different object in the array received from the server than the one defined in the original array
let c = [{ id: 2, selected: true, key: 'plan' }];
console.log(mergeArrays(a, c))
.as-console-wrapper {
max-height: 100% !important;
}
Hope this helps.
you must first determine which one is biggest then, if b is bigger than a:
let a = [{id: 1, selected: false, key: "plan"}];
let b = [{id: 1, selected: true, key: "plan", "text": "aaaa"}, {id: 2, selected: true}];
let bigger = a.length > b.length ? a : b;
let shorter = a.length < b.length ? a : b;
console.log(bigger.map(x => ({...x, ...shorter.find(y => y.id === x.id)})))
You can use destructure:
let c = [...a,...b];

Create array of objects from an object using javascipt?

My initial data is coming like this..
let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1 , 2 , 3]
};
I need output in this format..
FINAL_OUTPUT = [
{ labels: 'label1', children: 'child1', numbers: 1 },
{ labels: 'label2', children: 'child2', numbers: 2 },
{ labels: 'label3', children: 'child3', numbers: 3 }
];
Separated the keys and values from the initial object. But struck in creating array of objects from the same. Please help.
You could get the entries and map the inner array with the values at the given index.
let initial = { labels: ['label1', 'label2', 'label3'], children: ['child1', 'child2', 'child3'], numbers: [1, 2, 3] },
result = Object
.entries(initial)
.reduce((r, [k, a]) => a.map((v, i) => ({ ...(r[i] || {}), [k]: v })), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This should do exactly what you want, assuming all arrays have equal length. With a bit of creativity this algorithm can be generalized to objects of arbitrary fields count. Here's the fiddle.
let initial = {
labels: [
'label1',
'label2',
'label3'
],
children: [
'child1',
'child2',
'child3'
],
numbers: [
1,
2,
3
]
};
function convertObjectToArray(
labels,
children,
numbers
) {
let final = [];
for (let i = 0; i < numbers.length; i++) {
final.push({
labels: labels[i],
children: children[i],
numbers: numbers[i],
});
}
return final;
}
console.log(convertObjectToArray(
initial.labels,
initial.children,
initial.numbers
));
I don't know of any built in javascript functions, but you can create a for loop (assuming that all arrays in the object are of same length):
for(let i = 0; i < initial[Object.keys(initial)[0]].length; i++){
FINAL_OUTPUT.push({
labels: initial.labels[i],
children: initial.children[i],
numbers: initial.numbers[i]
});
}
let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1 , 2 , 3]
};
let keys = Object.keys(initial);
let keyLength = keys[0].length;
let sampleValueArray = initial[keys[0]];
let i = 0;
let result = sampleValueArray.map( (item,index) => {
let temp = {};
keys.forEach( key =>
temp[key] = initial[key][index]
)
return temp
})
console.log(result)
let initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1, 2, 3]
}
/* FINAL_OUTPUT = [
{ labels: 'label1', children: 'child1', numbers: 1 },
{ labels: 'label2', children: 'child2', numbers: 2 },
{ labels: 'label3', children: 'child3', numbers: 3 }
]; */
const result = Object.keys(initial).reduce((acc, x, i, keys) => {
const arr = keys.map((y, j) => initial[y][i]);
acc = [...acc, keys.reduce((acc_2, z, k) => ({
...acc_2,
[z]: arr[k]
}), [])]
return acc
}, [])
console.log(result)
let FINAL_OUTPUT =[]
for(let i =0;i<initial.length;i++){
FINAL_OUTPUT.push(
{labels: initial.labels[i],
children: initial.children[i],
numbers: initial.numbers[i]
})
}
You can use lodash's _.flow() to create a function that:
Uses to pairs to get an array of array of [key, [values]] pairs
Unzip to get separate keys from values
Destructure the keys and the values. Use _.unzip() to transpose the values, and then map, and use _.zipObject() with the keys to create the objects.
const { flow, toPairs, unzip, zipObject } = _;
const fn = flow(
toPairs, // convert to [key, values] pair
unzip, // transpose to array of keys, and array of values
([keys, values]) => // destructure keys and values
unzip(values) // transpose the values
.map(vals => zipObject(keys, vals)) // create object from keys and each set of values
);
const initial = {
labels: ['label1', 'label2', 'label3'],
children: ['child1', 'child2', 'child3'],
numbers: [1, 2, 3]
};
const result = fn(initial);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Merge array into array of objects

I'm working with API data and I'm trying to build an object combining multiple arrays of data.
Current Arrays:
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar']
let arrX = ...
Desired Outcome:
let desiredOutcome = [
{
name: "John",
arr1: "bar", ...
},
{
name: "Jane",
arr1: "foo", ...
},
{
name: "Doe",
arr1: "foobar", ...
}]
I've been trying to play around with Object.assign() but I haven't had any luck:
var merge = Object.assign(obj, arr1 )
Is there a method or methods I could use?
Use .map() to add each element.
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar']
let result = name.map((a,i)=>{a.arr1 = arr1[i]; return a})
console.log(result)
You can do it using Array.map
Try the following:
let name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
let arr1 = ['bar', 'foo', 'foobar'];
var result = name.map((o,i) =>Object.assign({"arr1" : arr1[i]},o));
console.log(result);
For an arbitrary count of arrays, you could take an array with the array of objects and the arrays of values and take short hand properties which preserves the name of the array and the values for adding to the result set with Object.assign.
var names = [{ name: "John" }, { name: "Jane" }, { name: "Doe" }],
arr1 = ['bar', 'foo', 'foobar'],
arrX = [1, 2, 3],
result = [names, { arr1 }, { arrX }]
.reduce((r, o) =>
(([k, a]) => a.map((v, i) => Object.assign({}, r[i], { [k]: v })))(Object.entries(o)[0])
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Maybe late but I'll provide some additional explanation on how to use .map:
The .map method creates a new array with the results of calling a provided function on every element in the calling array.
The method takes in one callback as argument. The callback itself can take predefined arguments. Here we will use the first two:
currentValue: e
index: i
Basically, the map method works by associating (literally mapping) each element of the looped array (here name) to the given returned value (here {name: e.name, arr1: arr1[i]}). Mapping is just a bijection between two arrays.
Another word on (e,i) => ({name: e.name, arr1: arr1[i]}):
It is the shorthand syntax called arrow function. It is similar to defining the callback function like so:
function(e,i) {
return { name: e.name, arr1: arr1[i] };
}
Full snippet will look like:
const name = [{name: "John"},{name: "Jane"},{name: "Doe",}]
const arr1 = ['bar', 'foo', 'foobar']
const result = name.map((e,i) => ({ name: e.name, arr1: arr1[i] }))
console.log(result)

Combine object and arrays

I'm trying to write a function that takes an array of objects, and an unlimited number of arrays, and combines them to form a single object. The inputs would follow this pattern:
let x = [{ name: 'Tom' }, { name: 'John' }, { name: 'Harry' }];
let y = [[1, 2, 3], 'id'];
let z = [['a', 'b', 'c'], 'value'];
combine(x, y, z);
With the second element of y and z acting as the object key. Using these arguments, the function should return the following array:
[
{
name: 'Tom',
id: 1,
value: 'a'
},
{
name: 'John',
id: 2,
value: 'b'
},
{
name: 'Harry',
id: 3,
value: 'c'
},
]
The index of the current object should be used to get the correct element in the array. I have made an attempt at the problem:
function combine(object, ...arrays) {
return object.map((obj, index) => {
let items = arrays.map(arr => ({
[arr[1]]: arr[0][index]
}));
return Object.assign({}, obj, { items });
});
}
This almost does the job, but results in the array items being hidden inside a nested items array, How can I solve this?
You had been assigning an object of object, and the result was a new object with the element items inside (another feature of object literal).
This approach use reduce instead of map and direct assign instead of object literal.
function combine(object, ...arrays) {
return object.map((obj, index) => {
const items = arrays.reduce((acc, arr) => {
acc[arr[1]] = arr[0][index] ;
return acc;
}, {});
return Object.assign({}, obj, items);
});
}
const x = [{ name: 'Tom' }, { name: 'John' }, { name: 'Harry' }];
const y = [[1, 2, 3], 'id'];
const z = [['a', 'b', 'c'], 'value'];
combine(x, y, z);
You can also use the spread operator in the Object.assign, like this:
function combine(object, ...arrays) {
return object.map((obj, index) => {
let items = arrays.map(arr => ({
[arr[1]]: arr[0][index]
}));
return Object.assign({}, obj, ...items);
});
}
This almost does the job, but results in the array items being hidden inside a nested items array
The problem is that items is an array, whereas you only need the current item inside of that particular map callback. No need to nest loops here.
Also I would recommend avoiding multiple properties per combine call. The resulting code would look like this:
function combine(objects, [values, key]) {
return objects.map((o, i) =>
Object.assign({[key]: values[i]}, o)
);
}
combine(combine(x, y), z);
If you then have multiple extensions to do, you can also use
[y, z].reduce(combine, x)
With map and computed keys, you can achieve this.
Here's a working example:
let x = [{
name: 'Tom'
}, {
name: 'John'
}, {
name: 'Harry'
}];
let y = [[1, 2, 3], 'id'];
let z = [['a', 'b', 'c'], 'value'];
let result = [];
x.map(function (el, index) {
result.push(el);
let index = result.length -1;
result[index][y[1]] = y[0][index];
result[index][z[1]] = z[0][index];
});
console.log(result);

Categories

Resources