How to remove unused objects after matching with another array? - javascript

I have an array of objects and another array of names like the following:
array of objects as file info objects
[
{ fieldname: 'banner', path: 'banner_img_path' },
{ fieldname: 'logo' , path: 'logo_img_path' },
{ fieldname: 'random', path: 'random_img_path' }
]
and here is my array of names
['banner', 'logo']
So how can i match or mapping those two variables! note that after mapping or matching, all not used objects should be deleted from the original array of objects?? i need to get object like that
{
banner: banner_img_path,
logo : logo_img_path
}
I'm able to do matching but i was stuck with erasing those not used objects.
Note: May be this question is duplicated but only because i don't know what is the name of this operation!
so please forgive me.

There is a simple solution by using functional native JavaScript functions.
const obj = [
{ fieldname: 'banner', path: 'banner_img_path' },
{ fieldname: 'logo', path: 'logo_img_path' },
{ fieldname: 'random', path: 'random_img_path' },
{ fieldname: 'other', path: 'random_img_path' }
];
const names = ['banner', 'logo'];
obj.map((o, i) => {
let match = false;
names.some((item) => {
if (item === o.fieldname) {
match = true;
return true;
}
});
if (!match) {
delete obj[i];
}
});
for (let o in obj) {
document.querySelector('p').innerHTML += obj[o].fieldname + '<br>';
}
<p></p>

I add the names you want to keep into a set for lookup. I then use forEach and check inside each object if the fieldname matches anything in the set of names. If there's a match, I add it to my newObj.
const namesToKeep = ['banner', 'logo']
const fileInfo = [
{ fieldname: 'banner', path: 'banner_img_path' },
{ fieldname: 'logo' , path: 'logo_img_path' },
{ fieldname: 'random', path: 'random_img_path' }
]
const names = new Set(namesToKeep)
const newObj = {}
fileInfo.forEach(obj => {
if (names.has(obj.fieldname)) {
newObj[obj.fieldname] = obj.path
}
})
console.log(newObj)

Related

JavaScript how to build array from nested Object using recursion without mutation

interface SubscriptionParams {
selectedProduct?: SubscriptionSelectedProduct;
}
interface SubscriptionSelectedProduct {
productId?: string;
pricingId?: string;
childProduct?: SubscriptionSelectedProduct;
}
function getChildIdRecursively(product: SubscriptionSelectedProduct, ids: string[]) {
if (product) {
ids.push(product.productId!);
product.childProduct && getChildIdRecursively(product.childProduct, ids);
}
}
function subscriptionProductsIds(subscription: SubscriptionParams): string[] {
let ids: string[] = [subscription.selectedProduct?.productId!];
if (subscription.selectedProduct?.childProduct) {
getChildIdRecursively(subscription.selectedProduct?.childProduct, ids);
}
return ids;
}
How to make this recursion without mutation, now I am mutating ids array.
I want to follow functional programming principles
I think you could resolve this by leveraging tree traversals IMHO.
const collectIds = (node) => {
const { id, children = [] } = node;
// const children = [product.childProduct] in your specific case
const ids = children.flatMap(collectIds);
return [id, ...ids];
};
const product = {
id: 'L1',
children: [
{ id: 'L1.1' },
{
id: 'L1.2',
children: [
{ id: 'L1.2.1', },
{ id: 'L1.2.2', },
{ id: 'L1.2.3', },
{ id: 'L1.2.4', },
{ id: 'L1.2.5', },
],
},
],
};
console.log(
collectIds(product),
);
I'd avoid creating a lot of unnecessary intermediate arrays, but to each their own. Making this "immutable" is as easy as returning a new array from the getChildIdRecursively. Also since you are basically duplicating the logic in subscriptionProductsIds you can remove that.
function getChildIdRecursively(product: SubscriptionSelectedProduct) {
if (product) {
let ids: string[] = [product.productId!];
if (product.childProduct) {
ids = ids.concat(getChildIdRecursively(product.childProduct));
}
return ids;
}
return [];
}
function subscriptionProductsIds(subscription: SubscriptionParams): string[] {
return getChildIdRecursively(subscription.selectedProduct)
}

Array methods for find a word in Javascirpt

I'm stuck working on something and need help. I want to find a word in a path and use it as an object.
I mean, there is array like this;
routes : [
0: {
...
path : 'x/x/create'
... }
1: {
...
path : 'y/y/y'
... }
]
and I want to use object with path : 'x/x/create'. And of course it s not always in the first place.
*Edit: x always changing so I need to find 'create' word
routes.find(i => i.path.includes('create')).word;
Try using Array.find
const routes = [
{ path: 'x/x/create' },
{ path: 'y/y/y' },
]
console.log(routes.find((route) => route.path === 'x/x/create'));
console.log(routes.find((route) => route.path === '/create'));
const routes = [
{
id: 1,
path: "x/x/create"
},
{
id: 2,
path: "y/y/y"
}
]
const result = routes.find(i => {
if (i.path.split('/')[2] === 'create') return i.id;
});
console.log(result); //{id:1, path:"x/x/create"}

How to iterate through out an array with a value path as reference

I don't really know how to express what I want, but I'll try.
So, I have an object with an array inside with the name of recipes, that I receive from my API, and a valuePath which is an object:
Object
{
recipes: [
{
test: {
items: [
{
type: 'test1',
}
]
}
}
]
}
ValuePath
{
"allRecipes": {
"array": "recipes",
"values": {
"allTypes": {
"array": "test",
"values": {
"type": "type"
}
}
}
}
}
Briefly what I have to do, is iterate over the array recipes through out the valuePath, dynamically, because the array and the values can change. I don't really know how to explain it better and how to iterate thought deeply nested objects/array's having a valuePath as a reference to find the values.
What I've tried so far...
export const test = (object, valuePath) => {
for (const prop in valuePath) {
object = object[valuePath[prop].array]; // find the array
if (Array.isArray(object)) {
object.forEach(objRef => {
console.log('valueRef', objRef);
});
}
console.log('props->', valuePath[prop].values); // find the values
}
};
I think i need a recursion, but have no clue how to do one.
If I understood your problem, this could be an implementation...
If you run it with your data and path, it will return test1.
// INPUTS
const data = {
recipes: [
{
test: {
items: [
{
type: 'test1',
}
]
}
}
]
}
const path = {
"allRecipes": {
"array": "recipes",
"values": {
"allTypes": {
"array": "test",
"values": {
"type": "type"
}
}
}
}
}
// this is just an helper method for arrays...
Array.prototype.first = function () { return this[0] }
// this is an helper function that tells us whether
// a path object is still traversable.
// from what I understood, if it contains an `array` property
// we should follow it...
const isTraversable = path => !!path.array
// this is the actual implementation of the algorithm
const traverse = (data, path) => {
const nextPath = Object.values(path).first()
if ( isTraversable(nextPath) ) {
const array = data[nextPath.array]
// I noticed that at a certain point in the data object,
// we need to traverse an array, and in another it is an
// object with an `items` property.
// this next lines helps determine how go down
const nextData = Array.isArray(array) ? array.first() : array.items
// we recurse on the traversed data and path
return traverse(nextData, nextPath.values)
}
return data.first()[path.type]
}
console.log(traverse(data, path))
Please try this, I hope it will help you..
let obj = {
recipes: [
{
test: {
items: [
{
type: 'test1',
},
],
},
},
],
};
obj.recipes.forEach(test => {
test.test.items.forEach(items => {
console.log(items.type);
});
});

Redux, normalised entities and lodash merge

I'm using Redux, React and Lodash with a fairly standard normalized entities store.
When I merge in new entities in a redux reducer, the references to all my existing entities change (despite not being modified), causing any pure components to re-render.
Is there an alternative to lodash's merge that can merge whilst maintaining the existing references to values that are not in the object being merged in?
let entities = {
[1]: {a: true },
[2]: {a: true, b: true },
}
let response = {
[2]: {a: false }
}
let newEntities = _.merge({}, entities, response)
console.log(entities[1] === newEntities[1]) // false
I can't use Object.assign/ES6 Spread here as then newEntities[2].b will be deleted.
I do realise there are alternative solutions such as custom sCU and reselect, however it would be much cleaner to take care of this at the reducer level rather than having to modify every single component that does an equality reference check on its props.
Use mergeWith with a customizer:
let keepRef = (objValue, srcValue) => (
objValue === undefined ? srcValue : _.mergeWith({}, objValue, srcValue, keepRef)
)
let newEntities = _.mergeWith({}, entities, response, keepRef)
I expanded on #Pavlo's awesome answer. I added support for arrays, and collections. I define a collection as an array of object's, where each object has an id key. This is very common in react/redux and normalized data.
import { mergeWith, isPlainObject, isEmpty, keyBy } from 'lodash'
// https://stackoverflow.com/a/49437903/1828637
// mergeWith customizer.
// by default mergeWith keeps refs to everything,
// this customizer makes it so that ref is only kept if unchanged
// and a shallow copy is made if changed. this shallow copy continues deeply.
// supports arrays of collections (by id).
function keepUnchangedRefsOnly(objValue, srcValue) {
if (objValue === undefined) { // do i need this?
return srcValue;
} else if (srcValue === undefined) { // do i need this?
return objValue;
} else if (isPlainObject(objValue)) {
return mergeWith({}, objValue, srcValue, keepUnchangedRefsOnly);
} else if (Array.isArray(objValue)) {
if (isEmpty(objValue) && !isEmpty(srcValue))return [...srcValue];
else if (!isEmpty(objValue) && isEmpty(srcValue)) return objValue;
else if (isEmpty(objValue) && isEmpty(srcValue)) return objValue; // both empty
else {
// if array is array of objects, then assume each object has id, and merge based on id
// so create new array, based objValue. id should match in each spot
if (isPlainObject(objValue[0]) && objValue[0].hasOwnProperty('id')) {
const srcCollection = keyBy(srcValue, 'id');
const aligned = objValue.map(el => {
const { id } = el;
if (srcCollection.hasOwnProperty(id)) {
const srcEl = srcCollection[id];
delete srcCollection[id];
return mergeWith({}, el, srcEl, keepUnchangedRefsOnly);
} else {
return el;
}
});
aligned.push(...Object.values(srcCollection));
return aligned;
} else {
return [ ...objValue, ...srcValue ];
}
}
}
}
Usage:
const state = {
chars: ['a', 'b'],
messages: [
{
id: 1,
text: 'one'
},
{
id: 2,
text: 'ref to this entry will be unchanged'
}
]
}
const response = {
chars: ['c', 'd'],
messages: [
{
id: 1,
text: 'changed ref text one'
},
{
id: 3,
text: 'three'
}
]
}
const stateNext = mergeWith({}, state, response, keepUnchangedRefsOnly)
Resulting stateNext is:
{
chars: [
'a',
'b',
'c',
'd'
],
messages: [
{
id: 1,
text: 'changed ref text one'
},
{
'id': 2,
text: 'ref to this entry will be unchanged'
},
{
'id': 3,
text: 'three'
}
]
}
If you want to keep undefined values, then replace mergeWith in customizer and your use case with assignWith. Example - https://stackoverflow.com/a/49455981/1828637

Go through object when keys are know

I have object
var routes = {
"home":{
hash: "/home",
children: {
"just-home": {
hash: "/home/just-home",
children: {...}
},
"sub-homea": {
hash: "/home/sub-homea",
children: {...}
}
},
"contact":{
hash: "/contact",
children: {
"just-contact": {
hash: "/contact/just-contact",
children: {...}
},
"sub-contact": {
hash: "/contact/sub-contact",
children: {...}
}
}
}
How i can set object to just-contact.children when i know for example - that first key is contact, and next just-contat.. ? I need to assign this object dynamically because the known keys will be all time different. So i need use any loop. something like this -
const pathArray = [contact,just-contact]
Object.keys(routes).map(function (item) {
if (routes[item] === pathArray[counter]){
ob = routes[item];
counter++;
}
})
but this will loop only once and it won't go to deep.
UPDATE for more clean explanation -
I will read from path location (localhost:3000/contact/just-contact) the values (contact,just-contact) , which i will save to array (pathArray=[contact,just-contact]), when the location path will be change, the keys in array will be change too. And i need to find children of last key, in this example children of just-contact key
Found simple solution -
pathArray.map(function (item) {
if (obj[item].hash === item){
obj = obj[item].children;
}
})

Categories