merge array object with three keys - javascript

There are two arrays of objects, a and b. Key is 'id' ,'isfix' ,'groupid'.
For example
a.id === b.id && a.isfix === b.isfix && a.groupid===b.groupdid
The sequence array is not the same.
I expected c.
I hope you don't use lodash. I like es6 or vanila js. thanks..
I think reduce and map and filter.. but Not as well as I thought.
I think make function...
input is a,b and output is c
var a = [
{
id:"555",
groupID:"10",
isFix:false,
tolerancePlus:5,
toleranceMinus:3
},
{
id:"123",
groupID:"10",
isFix:true,
tolerancePlus:"",
toleranceMinus:7
},
{
id:"555",
groupID:"10",
isFix:true,
tolerancePlus:11,
toleranceMinus:6
}
]
var b = [
{
id:"123",
groupID:"10",
isFix:true,
tolerance:{
min: null,
plus : null
}
},
{
id:"555",
groupID:"10",
isFix:false,
tolerance:{
min: null,
plus : null
}
},
{
id:"555",
groupID:"10",
isFix:true,
tolerance:{
min: null,
plus : null
}
},
]
var c = [
{
id:"123",
groupID:"10",
isFix:true,
tolerance:{
min: 7,
plus : 0 // if "" that value is 0
}
},
{
id:"555",
groupID:"10",
isFix:false,
tolerance:{
min: 3,
plus : 5
}
},
{
id:"555",
groupID:"10",
isFix:true,
tolerance:{
min: 6,
plus : 11
}
},
]

here's a way to do it :
it uses :
Array.reduce
Array.push
the ~ (bitwise not) operator
disclaimer : since it uses the ~ operator, it can (and will) break if your tolerance are not 32-bit integers (it's undefined behavior AFAIR)
// create your arrays
var a = [{id:"555",groupID:"10",isFix:false,tolerancePlus:5,toleranceMinus:3},{id:"123",groupID:"10",isFix:true,tolerancePlus:"",toleranceMinus:7},{id:"555",groupID:"10",isFix:true,tolerancePlus:11,toleranceMinus:6}]
var b = [{id:"123",groupID:"10",isFix:true,tolerance:{min: null,plus : null}},{id:"555",groupID:"10",isFix:false,tolerance:{min: null,plus : null}},{id:"555",groupID:"10",isFix:true,tolerance:{min: null,plus : null}},]
// loop over b, creating a new array
let c = b.reduce((acc, value) => {
// find an object from a which correspond to the current object
let linked = a.find(val => val.id === value.id && value.groupID === val.groupID && val.isFix === value.isFix)
// if it exists push it in the new array
if(linked) {
acc.push({
id: linked.id,
groupID: linked.groupID,
isFix: linked.isFix,
tolerance:{
min: ~~linked.toleranceMinus, // the ~~value here use some magic to transform
plus : ~~linked.tolerancePlus // everything that's not a number to 0
}
})
}
return acc
}, [])
console.log(c)
var wantedC = [{id:"123",groupID:"10",isFix:true,tolerance:{min: 7,plus : 0}},{id:"555",groupID:"10",isFix:false,tolerance:{min: 3,plus : 5}},{id:"555",groupID:"10",isFix:true,tolerance:{min: 6,plus : 11}}]
console.log(JSON.stringify(wantedC) === JSON.stringify(c))

Here is the Logic in vanilla JS:
var a = [
{
id: '555',
groupID: '10',
isFix: false,
tolerancePlus: 5,
toleranceMinus: 3
},
{
id: '123',
groupID: '10',
isFix: true,
tolerancePlus: '',
toleranceMinus: 7
},
{
id: '555',
groupID: '10',
isFix: true,
tolerancePlus: 11,
toleranceMinus: 6
}
]
var b = [
{
id: '123',
groupID: '10',
isFix: true,
tolerance: {
min: null,
plus: null
}
},
{
id: '555',
groupID: '10',
isFix: false,
tolerance: {
min: null,
plus: null
}
},
{
id: '555',
groupID: '10',
isFix: true,
tolerance: {
min: null,
plus: null
}
}
]
var c = a.map(data1 => {
const toleranceData = b.map(data2 => {
if (
data1.id === data2.id &&
data1.isfix === data2.isfix &&
data1.groupdid === data2.groupdid
) {
return {
tolerance: {
min: data1.toleranceMinus || 0,
plus: data1.tolerancePlus || 0
}
}
}
})
const { tolerance } = toleranceData.filter(d => d)[0]
const { id, groupID, isFix } = data1
return { id, groupID, isFix, tolerance }
})
console.log(c)

So we have 2 arrays of objects:
And we have a statement a.id === b.id && a.isFix === b.isFix && a.groupid===b.groupdid
To get what you need you can use arr.find() inside a arr.map() method and make our changes:
const a = [{ id: "555", groupID: "10", isFix: false, tolerancePlus: 5, toleranceMinus: 3 }, { id: "123", groupID: "10", isFix: true, tolerancePlus: "", toleranceMinus: 7 }, { id: "555", groupID: "10", isFix: true, tolerancePlus: 11, toleranceMinus: 6 } ]
const b = [{ id: "123", groupID: "10", isFix: true, tolerance: { min: null, plus: null } }, { id: "555", groupID: "10", isFix: false, tolerance: { min: null, plus: null } }, { id: "555", groupID: "10", isFix: true, tolerance: { min: null, plus: null } }, ]
let c = b.map(obj => {
const valuesObj = a.find(item => item.id === obj.id && item.isFix === obj.isFix && item.groupid === obj.groupdid);
if (valuesObj) {
obj.tolerance.min = valuesObj.toleranceMinus || 0;
obj.tolerance.plus = valuesObj.tolerancePlus || 0;
}
return obj;
})
console.log(c);

Related

Dealing with looping through nested arrays

I have data array, which has nested arrays inside (level1arr, leve21arr ...)
const data = [
{
level1arr: [
{
level2arr: [{ id: 1, isValid: true }, { id: 2, isValid: true }, { id: 3, isValid: true }],
},
{
level2arr: [{ id: 4, isValid: true }, { id: 5, isValid: true }, { id: 6, isValid: true }],
},
],
},
{
level1arr: [
{
level2arr: [{ id: 7, isValid: true }, { id: 8, isValid: true }, { id: 9, isValid: true }],
},
{
level2arr: [{ id: 10, isValid: true }, { id: 11, isValid: true }, { id: 12, isValid: true }],
},
],
},
];
I also have another array:
const invalidIds = [2,5]
I want to find elements with apecyfic id and change isValid property to false.
Is it better way than iteratinf over multiple nested arrays, like that:
data.forEach(lvl1 => {
lvl1.level1arr.forEach(lvl2 => {
lvl2.level2arr.forEach(element => {
// further nesting
});
});
})
Such iterating over multiple arrays is not good for performance. What is the best way to handle such case with nested arrays?
If it were nested arrays, you could use Array.prototype.flat(). However, you have a mix of nested objects and arrays. You will have to write a custom "flattener" for this data structure. Check this answer for details: how to convert this nested object into a flat object?
You can use recursion until you reach the level you need. Here's one way to do it.
const data = [{
level1arr: [{
level2arr: [{
id: 1,
isValid: true
}, {
id: 2,
isValid: true
}, {
id: 3,
isValid: true
}],
},
{
level2arr: [{
id: 4,
isValid: true
}, {
id: 5,
isValid: true
}, {
id: 6,
isValid: true
}],
},
],
},
{
level1arr: [{
level2arr: [{
id: 7,
isValid: true
}, {
id: 8,
isValid: true
}, {
id: 9,
isValid: true
}],
},
{
level2arr: [{
id: 10,
isValid: true
}, {
id: 11,
isValid: true
}, {
id: 12,
isValid: true
}],
},
],
},
];
const invalidIds =[2,5]
const findId = (object, key, value) => {
if (Array.isArray(object)) {
for (const obj of object) {
findId(obj, key, value);
}
} else {
if (object.hasOwnProperty(key) && object[key] === value) {
object.isValid = false;
return object
}
for (const k of Object.keys(object)) {
if (typeof object[k] === "object") {
findId(object[k], key, value);
}
}
}
}
invalidIds.forEach(id => findId(data, "id", id))
console.log(data)

I have list in the array .,how do I show one of its element should come first based on condition in mapping?

Here is the object.
var arr = {
firstValue:"xyz",
content:[
{
"value": "abc",
"checked": true
},
{
"value": "xyz",
"checked": false
},
{
"value": "lmn",
"checked": true
}
]
}
In this firstValue is xyz so while mapping xyz should come first like this:
var arr = {
firstValue:"xyz",
content:[
{
"value": "xyz",
"checked": true
},
{
"value": "abc",
"checked": false
},
{
"value": "lmn",
"checked": true
}
]
}
How to achieve this by using javascript,
Thanks.
const arr = {
firstValue: "xyz",
content: [
{
value: "abc",
checked: true,
},
{
value: "xyz",
checked: false,
},
{
value: "lmn",
checked: true,
},
],
};
const index = arr.content.findIndex((item) => item.value === arr.firstValue);
if (index !== -1 && index !== 0) {
arr.content.unshift(...arr.content.splice(index, 1));
}
It looks like you just want to sort the matching object to the top.
You can either use Array#sort(), though this will mutate the original object, so if it's in state you'll want to clone before sorting.
var arr = { firstValue: 'xyz', content: [ { value: 'abc', checked: true, }, { value: 'xyz', checked: false, }, { value: 'lmn', checked: true, }, ], };
const sorted = [...arr.content].sort((a, b) =>
(b.value === arr.firstValue) - (a.value === arr.firstValue));
console.log(sorted);
Otherwise you can find the index of the object using Array#findIndex() and then use Array#splice() and Array#unshift() to move it to the beginning of the array. Here declaring a utility function and returning a copy of the passed array to avoid mutation.
var arr = { firstValue: 'xyz', content: [ { value: 'abc', checked: true, }, { value: 'xyz', checked: false, }, { value: 'lmn', checked: true, }, ], };
const orderFirst = (a) => {
const res = [...a];
const i = a.findIndex((o) => o.value === arr.firstValue);
if (i && i !== -1) {
res.unshift(res.splice(i, 1));
}
return res;
};
const sorted = orderFirst(arr.content);
console.log(sorted);

how to check if object.values in array are the same in sequence

I have a case in my react app in which I have to check if values of object in array are the same in sequence and their other property increases squentialy by 1, and then if criteria is fulfilled push it to the new array. Number of values in sequence is provided from input.
For example user wants to push 4 objects to a new array that have their "status": true
objects = [
{
"someID": 0,
"status": false,
},
{
"someID": 1,
"status": false,
},
{
"someID": 2,
"status": true,
},
{
"someID": 3,
"status": true,
},
{
"someID": 4,
"status": true,
},
{
"someID": 6,
"status": true,
},
{
"someID": 7,
"status": false,
},
{
"someID": 8,
"status": false,
},
{
"someID": 9,
"status": true,
},
{
"someID": 10,
"status": true,
},
{
"someID": 11,
"status": true,
},
{
"someID": 12,
"status": true,
},
]
from my example objects with someID 2, 3, 4, 6, fulfill the criteria to have status: true but their someID values do not increase sequentially by 1 on the other hand those objects with someID 9, 10, 11, 12 have their status value as true and their someID values increase sequentially by 1. So the outcome should be
newArray = [
{
"someID": 9,
"status": true,
},
{
"someID": 10,
"status": true,
},
{
"someID": 11,
"status": true,
},
{
"someID": 12,
"status": true,
},
]
below a generic function that takes your array and iterates through a for loop. you pass down the property and expected matched value, just like the exact count in sequence expected:
const getSequence = (arr, count, key = 'status', value = true, id = 'someID') => {
let match = []
const isIDIncremental = (obj) => {
if(match.length === 0) return true
return obj[id] - 1 === match[match.length - 1][id]
}
for(const obj of arr) {
if(match.length === count) break
if(obj[key] === value && isIDIncremental(obj)) {
match.push(obj)
} else {
match = []
}
}
return match.length === count ? match : []
}
You could check status and the predecessor or successor.
const
objects = [{ someID: 0, status: true }, { someID: 1, status: false }, { someID: 2, status: true }, { someID: 3, status: true }, { someID: 4, status: true }, { someID: 5, status: false }, { someID: 6, status: false }, { someID: 7, status: true }],
result = objects.filter(({ someID, status }, i, a) => status && (
a[i - 1]?.status && a[i - 1]?.someID === someID - 1 ||
a[i + 1]?.status && someID + 1 === a[i + 1]?.someID
));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Filter things with changeable arguments

let products = [
{
"name": "Lenovo",
"price": "18000",
"model": "v580c"
},
{
"name": "Apple",
"price": "30000",
"model": "Iphone 6"
},
{
"name": "Nikon",
"price": "25000",
"model": "G290"
}]
I need to filter my products array with getProduct function, which accepts changeable list of arguments.
Argument can be either the name of the product and/or price within the minPrice, maxPrice, and/or model.
function getProduct(productName, minPrice, maxPrice, productModel) {
return products.filter(product => {
return product.price < maxPrice && product.price > minPrice && product.name == productName;
});
}
console.log(getProduct("Apple", 3540, 3000000000));
console.log(getProduct("Lenovo", 3540, 3000000000, "v580c"));
You can send an array of params as argument and write a logic to process them accordingly.
Sample:
function getProduct(array, params){
var list = array.filter(function(o){
return params.every(function(kv){
if(o.hasOwnProperty(kv.key)){
var cur = o[kv.key];
switch (kv.operation){
case ">": return cur > kv.value
case "<": return cur < kv.value
case "in": return cur.indexOf(kv.value) > -1
case "regex": return kv.value.test(cur)
default: return cur === kv.value
}
}
})
});
console.log(list);
return list;
}
var products=[{name:"Lenovo",price:"18000",model:"v580c"},{name:"Apple",price:"30000",model:"Iphone 6"},{name:"Nikon",price:"25000",model:"G290"}];
getProduct(products, [{key:"name", value: "Nikon"}])
getProduct(products, [
{key:"price", value: 20000, operation: ">"},
{key:"price", value: 40000, operation: "<"}
])
getProduct(products, [{key:"name", value: "e", operation: "in"}])
getProduct(products, [{key:"model", value: /\d{2,}/g, operation: "regex"}])
You can use an object with special structure for searching, if you need t search for more than one item. This proposal uses an object, with this structure for filtering:
{
name: 'Apple',
price: {
min: 3540, // both or a single border is possible
max: 60000
},
model: function (s) { return s.match(/s/); } // just search for a single letter
}
The algorithm looks for every property in search and if all comparisons are true, then the element is added to the result set.
function filter(array, search) {
return array.filter(function (a) {
return Object.keys(search).every(function (k) {
return (
a[k] === search[k] ||
typeof search[k] === 'object' && (
('min' in search[k]) && ('max' in search[k]) && search[k].min <= a[k] && a[k] <= search[k].max ||
('min' in search[k]) !== ('max' in search[k]) && (search[k].min <= a[k] || a[k] <= search[k].max)
) ||
typeof search[k] === 'function' && search[k](a[k])
);
});
});
}
var products = [{ name: "Lenovo", price: "18000", model: "v580c" }, { name: "Apple", price: "30000", model: "Iphone 6" }, { name: "Nikon", price: "25000", model: "G290" }, { name: "Foo", price: "10", model: "a1" }, { name: "Foo", price: "20", model: "a2" }, { name: "Foo", price: "30", model: "a3" }, { name: "Foo", price: "40", model: "a4" }, { name: "Foo", price: "50", model: "a5" }, { name: "Foo", price: "60", model: "a6" }, { name: "Foo", price: "70", model: "a7" }, { name: "Foo", price: "80", model: "a8" }, { name: "Foo", price: "90", model: "a9" }];
console.log(filter(products, { name: 'Foo', price: { min: 60 } }));
console.log(filter(products, { name: 'Foo', price: { max: 40 } }));
console.log(filter(products, { name: 'Foo', price: { min: 40, max: 60 } }));
console.log(filter(products, { name: 'Apple', price: { min: 3540, max: 60000 } }));
console.log(filter(products, { name: 'Lenovo', price: { min: 3540, max: 60000 }, model: 'v580c' }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Chai-related error message: "AssertionError: expected undefined to deeply equal"

I wrote a function that when given a list of objects and an
id, returns the same list, but with the corresponding object marked active
(all other objects should not be active).
const list = [
{ id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: true },
{ id: 4, active: false }
];
function markActive(list, value) {
list.forEach((id) => {
if (id.active = (id.id === value)) {
return true;
} else {
return false;
}
});
}
markActive(list, 2);
console.log(list)
Returns:
[ { id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: true } ]
It's working like a charm, except when I run "npm run [filename]" I get an error message:
Running Tests for [filename].
------------
[ { id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: true } ]
markActive
1) Case 1 (Given Sample)
2) Case 2 (String IDs)
0 passing (16ms)
2 failing
1) markActive Case 1 (Given Sample):
AssertionError: expected undefined to deeply equal [ { id: 1,
active: false },
{ id: 2, active: true },
{ id: 3, active: false },
{ id: 4, active: false } ]
at Function.assert.deepEqual
(node_modules/chai/lib/chai/interface/assert.js:216:32)
at Context.it (tests/test_02.js:23:12)
2) markActive Case 2 (String IDs):
AssertionError: expected undefined to deeply equal [ { id: '1',
active: false },
{ id: '2', active: true },
{ id: '3', active: false },
{ id: '4', active: false } ]
at Function.assert.deepEqual
(node_modules/chai/lib/chai/interface/assert.js:216:32)
at Context.it (tests/test_02.js:40:12)
Any idea what I'm doing wrong? Here's the code that sets up the tests:
const chai = require("chai");
const sinon = require("sinon");
const assert = chai.assert;
const markActive = require("../answers/02.js");
describe("markActive", () => {
it("Case 1 (Given Sample)", () => {
var list = [
{ id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: true },
{ id: 4, active: false }
];
var newList = markActive(list, 2);
var targetList = [
{ id: 1, active: false },
{ id: 2, active: true },
{ id: 3, active: false },
{ id: 4, active: false }
];
assert.deepEqual(newList, targetList);
});
it("Case 2 (String IDs)", () => {
var list = [
{ id: "1", active: false },
{ id: "2", active: false },
{ id: "3", active: true },
{ id: "4", active: false }
];
var newList = markActive(list, "2");
var targetList = [
{ id: "1", active: false },
{ id: "2", active: true },
{ id: "3", active: false },
{ id: "4", active: false }
];
assert.deepEqual(newList, targetList);
});
});
Your function isn't returning anything, so any variables you try to set to the result will be set as undefined.
To fix this, simply add a return statement to the end of your function.
function markActive(list, value) {
list.forEach((id) => {
if (id.active = (id.id === value)) {
return true;
} else {
return false;
}
});
return list; // return the updated list
}
NOTE: It's worth mentioning that because the array is referenced, you're modifying the values in-place. This is why the array you defined outside the function still had updated results even though you weren't logging the returned value. This can have unintended side effects if you were to run the markActive() function several times on the same list. If you want a new list to be returned, look into ways of copying and deep copying arrays in Javascript.

Categories

Resources