Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two immutable lists - how to make triple equality work?

Let's say we have an immutable object that is created using Facebook's great Immutable.js. I want to compare two lists that were produced using .map or .filter out of single source and make sure they are equal. It seems to me, that when using map/filter you are creating a new object that has nothing to do with a previous object. How can I make triple equality === work? Does it make sense at all?

var list = Immutable.List([ 1, 2, 3 ]);
var list1 = list.map(function(item) { return item; })
var list2 = list.map(function(item) { return item; })

console.log("LIST1 =", list1.toJS())      // [1, 2, 3]
console.log("LIST2 =", list2.toJS())      // [1, 2, 3]
console.log("EQUAL ===?", list1===list2); // false! Why? How?

You can play with it here: http://jsfiddle.net/eo4v1npf/1/

Context

I am building application using React + Redux. My state has one list that contains items, that have attribute selected:

items: [
    {id: 1, selected: true},
    {id: 2, selected: false},
    {id: 3, selected: false},
    {id: 4, selected: true}
]

I want to pass only selected ids to another container, so I tried it using simple connect:

function getSelectedIds(items) {
    return items
        .filter((item) => item.get("selected"))
        .map((item) =>  item.get("id"));
}

export default connect(
    (state: any) => ({
        ids: getSelectedIds(state.get("items"))
})
)(SomePlainComponent);

Problem is, if I set additional attributes:

{id: 1, selected: true, note: "The force is strong with this one"}

This causes state to change and SomePlainComponent to rerender, although the list of selected Ids is exactly the same. How do I make sure pure renderer works?

Edit with some additional info

For react pure rendering I was using mixin from react-pure-render:

export default function shouldPureComponentUpdate(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState);
}

As it is not aware of props that could be immutable, they are treated as changed, i.e.

this.props = {
    ids: ImmutableList1
}

nextProps = {
    ids: ImmutableList2
}

Although both attributes ids are equal by content, they are completely different objects and do not pass ImmutableList1 === ImmutableList2 test and shouldComponentUpdate returns true. @Gavriel correctly pointed that deep equal would help, but that should be the last resort.

Anyway, I'll just apply accepted solution and problem will be solved, thanks guys! ;)

like image 419
mseimys Avatar asked Jan 26 '16 10:01

mseimys


People also ask

Can we create immutable collection in java and how?

In Java 8 and earlier versions, we can use collection class utility methods like unmodifiableXXX to create immutable collection objects. If we need to create an immutable list then use the Collections. unmodifiableList() method.

Are lists immutable?

The list is a data type that is mutable. Once a list has been created: Elements can be modified. Individual values can be replaced.

Why use immutable collections?

The common use case for the immutable methods is a collection that is initialized from known values, and that never changes. Also consider using these methods if your data changes infrequently. For optimal performance, the immutable collections store a data set that never changes.


2 Answers

You can never have strict equality of immutable structures since an Immutable.js object, inherently, is unique.

You can use the .is function which takes two immutable objects and compares the values within them. This works because Immutable structures implement equals and hashCode.

var map1 = Immutable.Map({a:1, b:1, c:1});
var map2 = Immutable.Map({a:1, b:1, c:1});
console.log(Immutable.is(map1, map2));
// true
like image 159
Henrik Andersson Avatar answered Nov 15 '22 20:11

Henrik Andersson


If you want to keep your component pure and working with === then you can also denormalize your Redux state and store the selectedIds as a property in the store. Only update this list when an action occurs that adds/removes a selected item or toggles an item selection, but not when other arbitrary properties of the item are updated.

like image 32
Brandon Avatar answered Nov 15 '22 21:11

Brandon