Check equality of an objects array property and a nested object property - javascript

On my app I'm checking the equality between an objects array property and a nested object property - based on the results I return a new objects array.
The items objects array:
[{name: 'test345', value: 1, category: 'a'},{name: 'test2high', value: 2, category: 'b'},{name: 'test3low', value: 3, category: 'c'}]
The itemCategories object:
{testnum1: {name: 'test123', category: 'a', partialName: '345'}, testnum2: {name: 'test234', category: 'b', partialName: 'hight'}
I need to check the equality between the category properties and return the following objects array:
[{name: 'test', itemCategory: 'testnum1'},{name: 'test2', itemCategory: 'testnum2'}]
Below is what I did, it works fine, now I need to add a second check based on the item's name: I need to only return the items that their names include the partialName, any idea on how to adjust this?
export function getCategorizedItems(items) {
const categories = new Map(
Object.entries(itemCategories).map(([itemCategory, {category}]) => [
category,
itemCategory
])
)
return items
.map(item => ({
value: item.value,
category: categories.get(item.category)
}))
.filter(({category}) => category)
}

A basic approach be
create a comparison function
iterate over the items array
const items = [{name: 'test345', value: 1, category: 'a'},{name: 'test2high', value: 2, category: 'b'},{name: 'test3low', value: 3, category: 'c'}]
const itemCategories = {testnum1: {name: 'test123', category: 'a', partialName: '345'}, testnum2: {name: 'test234', category: 'b', partialName: 'hight'}}
const isEqual = (item, icItem) => {
return item.name.match(new RegExp(icItem.partialName + '$'))
&& item.category === icItem.category
}
const keyValues = Object.entries(itemCategories)
const res = items.flatMap(item => {
const itemCategory = keyValues.find(([, value]) => isEqual(item, value))
if (!itemCategory) return []
// return whatever you want based on item, key, value
return { item, itemCategory }
})
console.log('res', res)
If you prefer to use map (for the category lookup e.g) then you may store more data on the map (as value)
const items = [{name: 'test345', value: 1, category: 'a'},{name: 'test2high', value: 2, category: 'b'},{name: 'test3low', value: 3, category: 'c'}]
const itemCategories = {testnum1: {name: 'test123', category: 'a', partialName: '345'}, testnum2: {name: 'test234', category: 'b', partialName: 'hight'}}
const isEqual = (item, icItem) => {
return item.name.match(new RegExp(icItem.partialName + '$'))
&& item.category === icItem.category
}
const categoryToIc = new Map(Object.entries(itemCategories).map(([key, icItem]) => [icItem.category, { icItem, key }]))
const res = items.flatMap(item => {
const el = categoryToIc.get(item.category)
if (!el || !isEqual(item, el.icItem)) return []
// return whatever you want based on item, key, value
return { item, itemCategory: [el.key, el.icItem] }
})
console.log('res', res)

Related

Compare one array with a nested array and push value into a new array with same index in Javascript

I have 2 arrays
const arrayOne = [
{id: '110'},
{id: '202'},
{id: '259'}
];
const arrayTwo = [
{data: [{value: 'Alpha', id: '001'}]},
{data: [{value: 'Bravo', id: '202'}]},
{data: [{value: 'Charlie', id: '110'}]},
{data: [{value: 'Delta', id: '202'}]}
];
I need to create a new array comparing arrayOne[idx].id with arrayTwo[idx].data[idx2].id
Upon match, I need to create an array pushing value (arrayTwo[idx].data[idx2].value) to the new array against each index in arrayOne.
In this example, I would get newArray = [null, 'Bravo', null, Delta]
What I have tried:
arrayOne.map(item => ({
...item,
result: arrayTwo.filter(itemTwo => item.data.map(x => x.id).includes(itemTwo.id))
}));
and also
const newArr = [];
arrayOne.map((item, idx) => {
if (arrayTwo.filter(itemTwo => itemTwo.data?.map(x => x.id) === item.id)) {
newArr.push(arrayTwo.data[idx].value);
} else newArr.push(null);
});
To do this you can map arrayTwo and use .find() to search for the ID in arrayOne. I also mapped arrayTwo to the inner object to make the second map more concise.
const arrayOne = [
{id: '110'},
{id: '202'},
{id: '259'}
];
const arrayTwo = [
{data: [{value: 'Alpha',id: '001'}]},
{data: [{value: 'Bravo',id: '202'}]},
{data: [{value: 'Charlie',id: '777'}]},
{data: [{value: 'Delta',id: '202'}]}
];
const result = arrayTwo
.map(obj => obj.data[0])
.map(obj => (arrayOne.find(v => v.id === obj.id) && obj.value) || null)
console.log(result)
Use map to iterate over each element of arr1 and return a new array.
Reassemble the data attribute array of each element in the arr2 array
using map and flat
When arr1 traverses, you can get the current element id, use filter
to filter the combined data array, and return an element array that matches
the current element id.
Based on the case where the id is not matched, use the optional chain operator to get the value.
When returning
if you want to get the element array of the id and
value attributes, use conditional (ternary) operator, when it doesn't match, return the original element,
when it matches, use spread syntax, copy the current element
attribute, and add the value attribute
if you only want to get an
array of matching results, just return the value,
remember to use the optional chain operator to convert the unmatched
value to null.
const arr1 = [
{ id: '110' },
{ id: '202' },
{ id: '259' }
];
const arr2 = [
{ data: [{ value: 'Alpha', id: '001' }] },
{ data: [{ value: 'Bravo', id: '202' }] }
];
const result1 = arr1.map(o1 => {
const data = arr2.map(o2 => o2.data).flat();
const value = data.filter(o2 => o2.id === o1.id)[0]?.value;
return value ? {...o1, value} : o1;
});
const result2 = arr1.map(o1 => {
const data = arr2.map(o2 => o2.data).flat();
const value = data.filter(o2 => o2.id === o1.id)[0]?.value;
return value ?? null;
});
[result1, result2].forEach(r => console.log(JSON.stringify(r)));
You can try this easy line of code :
const arrayOne = [{ id: '110' }, { id: '202' }, { id: '259' }];
const arrayTwo = [{ data: [{ value: 'Alpha', id: '001' }], }, { data: [{ value: 'Bravo', id: '202' }] }];
let result = arrayOne.map(el => {
let found = arrayTwo.find(f => f.data.at(0)?.id == el.id)?.data.at(0)?.value;
return { id: el.id, value: found ?? null};
});
console.log(result);

Convert object into array of object values

In React I have a state:
const [selecteditems, setSelectedItems] = useState()
On form submit I have an object that looks like this {items: {item1: true, item2: true, item3: false}}
I need to turn this object into an array of objects that looks like this: [{ name: 'item1', price: ''}, {name: 'item2', price: ''}] to use with setSelectedItems
Then I would like to be able to update prices of individual items using setSelectedItems. How would I achieve this?
To recap, first I need to convert an object into an array of objects, then I need to update individual price values in the array of objects separately.
You can use filter and map. For updating an object, find can be used.
const obj = {items: {item1: true, item2: true, item3: false}};
const res = Object.entries(obj.items).filter(([k,v]) => v)
.map(([k]) => ({name: k, price: ''}));
console.log(res);
//update
let name = 'item2';
res.find(x => x.name === name).price = '$10';
console.log(res);
Or you can use reduce() also.
const value = {items: {item1: true, item2: true, item3: false}}
// [{ name: 'item1', price: ''}, {name: 'item2', price''}]
const res = Object.entries(value.items).reduce((acc, [k,v]) => {
return v ? acc.concat({ name: k, price: '' }) : acc;
},[])
console.log(res);
const items = {items: {item1: true, item2: true, item3: false}}
const itemArray = [];
Object.keys(items.items).forEach((key) => {
if (items.items[key])
itemArray.push({
name: key,
price: '',
});
});
setSelectedItems(itemArrary);
Update price
const name = 'item1';
const _itemArray = [...itemArray];
const index = _itemArray.findIndex(item => item.name === name);
if (index !== -1) {
_itemArray[index].price = '$100';
setSelectedItems(_itemArray);
}
you can to this
const price = [{ name: 'item1', price: ''}, {name: 'item2', price: ''}]
const newPrice = {
name: "item1",
price: "$1000"
}
const results = price.map((x) => x.name === "item1" ? newPrice : x)
console.log(results)

Filter array of objects based on multiple values from another array of objects

I have an array of objects,
c = [
{
name: 'abc',
category: 'cat1',
profitc: 'profit1',
costc: 'cost1'
},
{
name: 'xyz',
category: '',
profitc: 'profit1',
costc: ''
},
{
name: 'pqr',
category: 'cat1',
profitc: 'profit1',
costc: ''
}
]
Now I want to filter array based on another array of objects, the second array of objects is,
arr = [
{
type:'profitc'
value: 'profit1',
},
{
type:'category'
value: 'cat1',
}
]
Now the arr is shown in dropdown with multiple select option and the value of key value in the object is shown to the user i.e profit1, cat1, etc. So if a user selects profit1 and cat1, then I need to filter the array c such that, the output looks like this.
c = [
{
name: 'abc',
category: 'cat1',
profitc: 'profit1',
costc: 'cost1'
},
{
name: 'pqr',
category: 'cat1',
profitc: 'profit1',
costc: ''
}
]
I tried doing this.
let result = c.filter(e => {
let istruecat = true
//arr is chosen value from user.
arr.forEach(element => {
istruecat = e[element.type] == element.value;
})
return istruecat;
})
But when I do this I get all the objects from the c array. What am I doing wrong here? Is there a an way to do this using lodash.
-You compute istruecat based only on the last entry in arr. You should use reduce instead to accumulate value:
let result = c.filter(e => arr.reduce((acc, element) => acc && e[element.type] === element.value, true))
You could filter the array by checking all given key/values pairs with the data's objects.
var data = [{ name: 'abc', category: 'cat1', profitc: 'profit1', costc: 'cost1' }, { name: 'xyz', category: '', profitc: 'profit1', costc: '' }, { name: 'pqr', category: 'cat1', profitc: 'profit1', costc: '' }],
filters = [{ type: 'profitc', value: 'profit1', }, { type: 'category', value: 'cat1' }],
result = data.filter(o => filters.every(({ type, value }) => o[type] === value));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here's an implementation of reducing the list c based in the given values by the filter array arr. Note that the output is a new list based on the initial content in c.
result = arr.reduce((acc, curr) => {
acc = acc.filter(item => {
return item[curr.type] === curr.value;
});
return acc;
}, c);
Or a recursive and imo readable solution:
function myFilter(c, [first, ...rest]) {
if (first) {
const {type, value} = first;
// Recursively call myFilter with one filter removed
return myFilter(c, rest)
.filter(x => x[type] === value);
} else {
// Nothing left to filter
return c;
}
}
myFilter(c, arr);

Creating a key:value object from a flat object

I'm using Javascript to get data from an API.
I get the following list of objects.
[
{name: 'Cat', value: 2, extra: 'data'},
{name: 'Dog', value: 3, extra: 'data'}
]
I'm trying to get to the following form
[
cat: {value: 2},
dog: {value: 3}
]
I'm currently trying using map but I don't see where the key part comes in.
return data.map((item) => ({
value: item.value
}));
var data = [
{name: 'Cat', value1: 2, value2: 3},
{name: 'Dog', value1: 3, value2: 4}
]
var result = data.map((item) => {
var name = item.name
delete item.name
//if you want to modify some property
item.value1 = item.value1 + "$"
return {[name]: {...item}}
})
console.log(result)
const result = {};
for(const {name, value} of array)
result[name] = { value };
A plain old for loop does it. With functional programming you would have to use reduce to boil it down to one single object, map always returns an array.
const result = array.reduce((res, {name, value}) => ({ ...res, [name]: {value}}), {});
You can use Object.assign() with map() method to return object as a result.
const data = [{name: 'Cat', value: 2},{name: 'Dog', value: 3}]
const result = Object.assign({}, ...data.map(({name, value}) => ({[name]: {value}})))
console.log(result)

Javascript how to filter an array using forEach() inside filter()

I have an array of objects and I'd like to filter it based on the objects property values. I'd like to filter it by different properties, so it needed to be dynamic. For this I have an input field where I type and then filter the array. So, let's say I have these 2 different arrays:
const array_one = [
{id: 1, code: 'ABC123', name: 'John'},
{id: 2, code: 'DEF456', name: 'Stew'},
// ...
];
const array_two = [
{id: 1, value: '012345', company: 'Company 01' },
{id: 2, value: '678910', company: 'Company 02' },
// ...
];
I want a function where I can filter the first array based on the name, also If I want to filter the second array, I want to filter it by the value.
For this, I built this function:
filterArray(array: Array<any>, fields: Array<any>, value: string) {
value = this.convertString(value);
array = array.filter((item) => {
fields.forEach(obj => {
if ( item[obj] ) {
const _newObj = this.convertString(item[obj]);
if ( _newObj.indexOf(value) !== -1 ) {
console.log(item);
return item;
}
}
});
});
return array;
}
// convertString() is just another function to replace accents, spaces, etc...
Then I call it like this:
filterArray(originalArray, ['name'], valueFromInput);
// or...
filterArray(originalArray, ['value'], valueFromInput);
// or even...
filterArray(originalArray, ['value', 'company'], valueFromInput);
But the array filtered is always returnin empty, even if the console inside the indexOf verification prints the correct object on the console.
What am I doing wrong here? Because it's filtering properly, I have manually checked it, but it doesn't add to the new filtered array.
You can iterate the fields using Array#some, and if one of them is equal to value return the item:
const array_one = [
{id: 1, code: 'ABC123', name: 'John'},
{id: 2, code: 'DEF456', name: 'Stew'}
];
const array_two = [
{id: 1, value: '012345', company: 'Company 01' },
{id: 2, value: '678910', company: 'Company 02' }
];
const filterArray = (array, fields, value) => {
fields = Array.isArray(fields) ? fields : [fields];
return array.filter((item) => fields.some((field) => item[field] === value));
};
console.log(filterArray(array_one, 'name', 'Stew'));
console.log(filterArray(array_two, ['id', 'company'], 2));
console.log(filterArray(array_two, ['id', 'company'], 'Company 02'));
If you want to make the filter about more than one filed then the value that you send it to the function, should be also array.
In my code below I assume that you want to return the object that Achieves all conditions (contains all properties that you send to the function with the same value)
const array_one = [
{id: 1, code: 'ABC123', name: 'John'},
{id: 2, code: 'DEF456', name: 'Stew'},
];
const array_two = [
{id: 1, value: '012345', company: 'Company 01' },
{id: 2, value: '678910', company: 'Company 02' },
];
function filterArray(array, fields, value) {
array = array.filter((item) => {
const found = fields.every((field, index) => {
return item[field] && item[field] === value[index]
})
return found
});
return array;
}
console.log(filterArray(array_one, ['name'], ['Stew']));
console.log(filterArray(array_two, ['id', 'company'], [1,'Company 01']));

Categories

Resources